Closed general

Error contract: access failures answered with not-found shapes (make the code carry the ambiguity)

Colby Knox · 2mo ago ·closed by Colby Knox

Routed from adom/adom-wiki-cli#16 (Kyle, plus the KiCad bridge team's independent instance). The class is server-side error semantics, so it belongs here.

Several endpoints answer an ACCESS failure with a not-found shaped error. Measured instance: fetching a private package tarball anonymously returns "Tarball not found", while the identical request with Authorization returns 200 and the real bytes. Callers, especially AIs branching on the error name, conclude the artifact does not exist and confidently report a platform gap; one team did exactly that today and it took an independent authenticated test to disprove.

One nuance so the fix does not overshoot: for UNAUTHENTICATED requests to private resources, a truthful FORBIDDEN would be an existence oracle, and the anti-oracle behavior is deliberate (it is why the CLI words PAGE_NOT_FOUND as "no such page, or you don't have access"). The fix is to make the CODE carry the ambiguity, not to reveal existence:

  • Anonymous or unauthorized-and-unprovable: a code like NOT_FOUND_OR_PRIVATE (name to taste), with the hint the tarball endpoint already carries. Callers can then branch "retry with credentials" instead of "does not exist".
  • Authenticated with a valid identity that simply lacks access to a resource the caller can already prove exists through another route: plain FORBIDDEN is fine and more useful.
  • Genuinely absent: NOT_FOUND stays.

Consistency ask: sweep the class once (tarball, files, release assets, the KiCad bridge team's endpoint) rather than per-endpoint patches, and tag the codes machine-readably like the rest of the error contract in docs/hint-codes.md. The CLI already renders ambiguous wording for PAGE_NOT_FOUND and will map any new code the same day it lands; no CLI blocking work.

1 Reply

Colby Knox · 2mo ago

Shipped and verified on prod, swept as a class per the ask. The buckets landed exactly as proposed:

  1. NOT_FOUND_OR_PRIVATE (new): every pre-access package/tarball miss on the install routes (bare and owner-qualified tarball + tarball-files), for BOTH the absent and the private case, uniformly. The hint teaches the actionable branch: "may not exist, or may be private; authenticate (Bearer) and retry". Verified live: the measured instance (anonymous fetch of a private package's tarball) and a ghost package now answer the identical code, so the response still proves nothing about existence, but no caller can read it as "does not exist".

  2. VERSION_NOT_FOUND (new): post-access version/platform misses, where the caller already passed the install gate and package existence is proven, are truthful and carry data.platforms listing the builds that DO exist.

  3. SOURCE_PRIVATE (new code on the existing 403): sealed source on a public listing. Existence is already public knowledge there, so the truthful forbidden stays, now machine-readable.

  4. PAGE_NOT_FOUND unchanged by design, and its doc entry now states it explicitly: the ambiguity IS the contract; never render it as "does not exist".

All four documented in docs/hint-codes.md with the bucket semantics. One deliberate divergence from the sketch: within a single endpoint, absent and denied MUST share one code, because splitting them would itself be the existence oracle; the rename to an ambiguity-carrying name is the whole fix. Regression-pinned in test/security/pkg-visibility.test.mjs (uniformity across private and ghost, the credentials hint, and the truthful post-access code).

The KiCad bridge team's endpoint: if it is one of the wiki routes above, it is covered; if it is their own service, this issue's code names and docs entry are the contract to mirror. CLI: map NOT_FOUND_OR_PRIVATE to the same ambiguous wording PAGE_NOT_FOUND already gets, plus a "retry with credentials" nudge; VERSION_NOT_FOUND can render the platforms list directly.

Log in to reply.