api: machine-readable code and args on every error body #224
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#224
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Every error the harness's HTTP API answers with gains a machine-readable
codeand itsargs, next to the English message it carries today. This is the server half of translating the editor: the SPA will translate errors by code (see the follow-up issue on the editor's i18n runtime), while the server itself never translates anything.Current behavior
Errors reach the SPA as English prose only:
{"error": "<message>"}fromwriteJSONError, with the message built byfmt.Sprintfat the call site, in handlers (internal/server) or in the operation packages (pageops.OpError, and the equivalents inelemops,mediaops).{"error": "<field>: <msg>", "errors": [{"field", "msg"}]}fromwriteFieldErrors, the 422 field-error contract, with eachelement.FieldError.Msgcoming from a field type's rule ("must be a number","must be one of: a, b", …) or frompageops.FieldErrors.staticFeedbackMessage,emptyPublishMessageininternal/server/publish.go) and a handful of built-in labels the server composes ("Home","Section","Enabled","Disabled","Yes","No").The SPA (
web/src/api/client.ts,ApiError) shows these strings verbatim and never parses them. Roughly 170 message sites: ~87 operation errors, ~54 handler errors, ~25 field rules, plus the publish messages and labels.Desired behavior
code(a stable, dotted identifier such aspage.exists,field.number,root.delete) andargs(a JSON object of the values the message interpolates: page names, language codes and labels, field names, option lists as arrays). The Englisherrorstring is unchanged, byte for byte, for every existing message. Agents, curl and the Go tests see no difference beyond the two new fields.errorslist carriescodeandargsthe same way, next to the unchangedfieldandmsg.codefield next tomessage; acodenext tolabel).web/src/that the SPA's catalog parity test will read. The test fails when the file on disk is stale, as the agents-doc drift test does forscaffold/site/AGENTS.md.Decisions
errorandmsgstrings stay exactly as they are — the CLI, agents reading the API and every existing test rely on them. Translation is additive.SchemaLoadErrorlists GET /api/elements, /api/backendlayouts and /api/modules return, shown bySchemaErrorBanner) and Hugo's build output are developer-facing diagnostics and stay English without a code. Same for every CLI message.[Translate to <label>:]placeholder the harness writes into content files is a machine marker and must not change.Key interfaces
errorResponseandfieldErrorsResponseininternal/server/server.go— gaincodeandargs.element.FieldError(internal/element/values.go) — gains code and args; itsrules()implementations per field type (ftype_*.go) produce them.pageops.OpErrorandpageops.FieldErrors(internal/pageops/errors.go) and theerrInvalid/errConflict/… constructors;elemopsandmediaopshave the same pattern.writeJSONErrorandwriteFieldErrors(internal/server/server.go),writeOpError(internal/server/pageops.go) — the write boundaries.{"error": …}shape and the 422 field-error format are documented in the "How it works" and API sections) must documentcodeandargs.Acceptance criteria
codeandargs; every entry in a 422errorslist does too.error/msgstring is unchanged (existing Go tests pass without edits to their expected strings).web/src/and fails when the committed file is stale.codeandargsfields on both error shapes.How to verify
go test ./...passes;make buildproduces the binary.curl -s http://127.0.0.1:8090/api/...for a few refusals (rename the root page, create a page whose slug exists, save a number field with text) showscodeandargsnext to the unchanged message.go test ./...: the export test fails, naming the file.Assumptions to check
elemopsandmediaopsbuild their errors the waypageopsdoes — look before assuming the constructors line up."error":); there may be more than one place.Out of scope
Blocked by
None - can start immediately
dominik.polakovics referenced this issue2026-10-12 14:22:49 +02:00