Repository navigation
fix(api): name repository, standing approval and credential source field refusals - #3725
Conversation
…eld refusals Several OCC refusals that depend only on the request, or on state the caller already proved access to, were thrown as plain ScopeViolationError, ResourceConflictError or DependencyUnavailableError, so the API answered the generic 404 "not found", 409 "already exists" or a retryable 503. Retype them to verbatim classes with text that says what to change: credential source catalog fields (400 with the field path), repository selections (400), standing repository approval conflicts (409), a ServiceAccount Harness source in another Namespace (400), and a Compute Driver without repository credentials (409). Finding 2536.
…hange notice Move the two oldest 2026-10-24 notices to the earlier page to keep the main page under the length limit.
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs maintainer review before merge. What this changesThis PR replaces generic API refusals with specific field, repository-selection, standing-approval, and Compute-capability explanations. Example: Create an openai credential source without secrets.api_key.
Review scores
ProductKind: Bug fix · Worth it: Yes · Fix scope: Complete Merge readiness✅ Ready for maintainer review Keep this PR open: it makes existing refusals actionable without changing admission rules, and no introduced correctness or security defect was found. Priority: P2 Before mergeNone. FindingsNone. Tests
Agent review detailsHow this fits togetherThe controller authorizes API requests, OCC validates resource operations, and the HTTP error mapper converts typed refusals into public status codes, messages, and field details. flowchart LR
Request[API request] --> IAM[Exact operation authorization]
IAM --> OCC[OCC admission checks]
OCC --> Mapper[Typed error mapping]
Mapper --> Response[HTTP refusal and recovery guidance]
Technical reviewBest possible solution: Land the focused typed-refusal changes with the documented status changes and existing disclosure boundaries preserved. Do we have a high-confidence way to reproduce the issue? Current-main source shows the catalog and repository refusals reaching generic error mappings; the changed tests exercise these paths, but no target code was run during this read-only review. Is this the best way to solve the issue? Using existing typed errors is a focused solution that retains authorization order, avoids a global message pass-through, and escapes field-detail paths. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 5632162a3197. Provenance checked
TestingProof path: in-process harness. SecurityNone. EvidenceWhat I checked:
Likely related people:
LabelsLabel changes:
Label justifications:
Rating scale6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior. WorkflowClawSweeper edits this one comment on every review. Comment Reviewed October 26, 2026, 2:00 AM ET / 06:00 UTC. |
Problem
requestFailureinapps/controller/src/http/errors.tsreplaces the message of a plainScopeViolationError(404 "The requested platform resource was not found."), a plainResourceConflictError(409 "The requested platform resource already exists.") and anon-whitelisted
DependencyUnavailableError(503 "A required platform dependency isunavailable."). Several OCC refusals that a caller can fix were thrown with those classes, so
the specific text never reached the caller (finding 2536, first slice):
config/secretsfield answered 404.not approve, or with both
repositoryAccessandrepositoryBindings, answered 404.refusal at approve and at deploy).
harnessAuth.sourcenaming a ServiceAccount in another Namespace answered 404.credentials (Docker, SSH) answered a retryable 503.
The generic 404 for missing or unreadable resources is deliberate (no existence oracle), so
this is not a global pass-through: each site was checked for what the caller has already
proved before the throw.
Change
13 throw sites, each retyped to a class
requestFailurealready returns verbatim:CredentialSourceConfigError, detail/config/<field>or/secrets/<field>, e.g.Credential source type openai has no field secrets.extra: remove it. Its secrets fields: api_key.RepositoryBindingSelectionErrorwith a fixed text per ruleResourceStateConflictErrornaming the stepSecretBindingValidationErrorResourceStateConflictErrornaming the admin fixDisclosure rules kept:
ServiceAccount Namespace check, which reads only the request and runs before any account
lookup.
text is the same for unknown, foreign-Namespace, wrong-profile and duplicate refs. Standing
approval texts say an approval blocks, but give no approval ID (approvals are read with Agent
administer), as
SecretReferencedErrorcounts referents the caller cannot read.CredentialSourceConfigErrorgains an optionalgroup(defaultconfig), and HTTP now capsits message and escapes its detail path. This class lives in OCC; the Driver contract is
unchanged.
Docs: credential source and Agent error rows, the standing approval guide, and a breaking-change
notice for the status changes (the two oldest 2026-10-24 notices move to the earlier page to
keep the main page under the length limit).
The other reachable sites from the audit (protected Credential Proxy deploy admission,
prepared-source update, the two user-worded 503s, Driver-level
ScopeViolationErrors andResourceConflictErrors) are listed for a follow-up.Verification
tests/conformance/credential-source-occ.test.mjs: exact status, text and detail path forthe unknown and required field refusals on create and update; a caller without the source
grant gets the same answer for an existing and a missing source (update) and Namespace
(create), with each request's own ID hidden.
tests/integration/repository-credentials-admission.test.mjs: exact 400 text at create,PATCH, provisioning and deploy after a policy change; 409 text for SSH Compute; a caller
without grants gets the same answer for an existing and a missing Agent on PATCH and deploy,
while the administrator gets the named refusal for the same request.
tests/integration/occ-api.test.mjs: the ServiceAccount Namespace rule at Agent create(existing and missing account answer alike) and at provisioning (a missing account in the
route Namespace stays 404).
tests/integration/postgres-standing-approval-api.test.ts: exact text for each standingapproval refusal, and a deploy after a policy change answers 409 without the approval ID.
tests/integration/postgres-protected-credential-free.test.mjs: a credential-free sourcewithout execution authority now answers 400 naming
config.authority(the catalog requiresit) instead of 404.
tests/integration/egress-prepared-github-metadata.test.mjs: the same fora
github-app-metadatasource without consent.tests/browser/console-agent-repositories.test.ts: the catalog-shrink recovery now sees 400 atAgent create; the console already treats 400 as a known rejection and offers Reload
repository choices as before.
running the field check ahead of the source grant and naming the stored source fails the
identical-answer test (400 vs 403); making the deploy policy text echo the approval ID fails
the standing approval test.
run);
postgres-platformlane,tsc --buildwith tests andeslint on the touched files pass.
Risks
status or generic text need the breaking-change notice. The console shows these as before
(its 400/409 messages are its own).
instead of retrying; every retry would have failed the same way.
GET .../repository-approval-scopesand a provisioning status read whose stored repositoryselection the policy no longer approves now answer 400 with the named rule instead of 404.
administer, that an approval exists (no ID). That caller was already refused; only the
reason is new.