ai-coding-minesIndexGitHub

The reason for a `400` is on the exception object

Python and databases

Symptom

A message-sending API returned 400 Bad Request. Suspecting the payload, escaping, length and special characters were all adjusted in turn. None of it mattered.

Cause

The reason was in the response body.

{"description": "Bad Request: PEER_FLOOD"}

It was a per-sender rate limit for too many messages in a short window — nothing to do with content. The code caught HTTPError, took the status code, and discarded the body.

★★ The exception object carries the response. One e.read() reveals it. Take only the status code and the reason disappears entirely.

Diagnosis — same input, different sender

The identical body succeeded from a different sender. That is the evidence for "not a content problem."

Re-running with exactly one variable changed separates a content problem from a state problem.

Response — three stages

  1. A fixed delay after each successful send
  2. On detecting the limit, a longer wait and retry
  3. Still failing? Send from a different sender

★ Channels that had been split by purpose became availability redundancy here. Splitting creates alternate paths as a side effect — with one sender there is no stage 3.

★★ Aside — the test consumed the production quota

The limit was hit by testing. Repeatedly clearing the idempotency file (the "already handled" list) and re-running meant sending the same notifications over and over.

★★ When testing something that consumes an external quota, "clear the processed list and run it again" multiplies your outbound volume. That is a path where testing breaks production. Keep a separate dummy recipient that costs no quota.