Creating an issue with an unknown label answers 404, not 422 #186

Closed AtlantisPleb opened this 4d ago

Evidence

Shipped in the release at b77305e, promoted 2026-08-26.

1 builds receipt

2 deploys receipts

POST /api/v3/repos/{owner}/{repo}/issues answers 404 Not Found when a label
in the request body does not exist. The response names the repository path and
nothing else:

{"code":"not_found","message":"Not Found","errors":{},
 "status":404,"documentation_url":"https://openagents.com/api/v3"}

Both the repository and the caller's authority are fine. The label is the only
thing missing, and the response does not say so.

How it reads from outside

I hit this while filing an issue with --label area:data-rights. 404 on a
POST to a path whose GET works reads as "the write route is not
implemented" or "this token cannot reach it" — I checked the router, confirmed
the route existed, checked openagents auth status, and concluded the store
token could not reach the :agent_participation_api pipeline. All of that was
wrong. The token holds forge:write and reaches the route. The label did not
exist.

Cause

The controller rescues Ecto.NoResultsError and renders 404. The rescue is
correct for the repository lookup, which is why it is there, but it also
catches the label resolution further in, so two unrelated failures leave by the
same door.

Why this one is worth fixing rather than documenting

The 404 is indistinguishable from the response for a repository that does not
exist, which the API returns deliberately so a private repository's existence
is not disclosed. That deliberate ambiguity is load-bearing, and this defect
hides behind it — the caller cannot tell a privacy decision from a typo, so the
honest error is unreachable. GitHub answers 422 Unprocessable Entity with a
errors[].field naming the label for exactly this case.

Done when

  • A request naming a label that does not exist answers 422 with the offending
    label named, and the same for assignees and milestones if they share the
    rescue.
  • A request for a repository the caller cannot see still answers 404 with no
    disclosure, and a proof asserts the two are different responses.
  • The rescue is narrowed to the lookup it was written for, so a third
    Ecto.NoResultsError added later does not silently join them.
  1. AtlantisPleb opened this issue 4d ago
  2. AtlantisPleb closed this as completed in adc413e 4d ago
Sign in with GitHub to comment on this issue.