Repository navigation
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Users are shown an error ID (the request ID) when a request fails, but there was no way to look it up: the underlying cause only reached stdout, and none of the admin log-center tabs indexed it. Several failure paths also recorded no cause at all, so even the stdout log could not explain the failure.
This change makes the failure cause a persisted, searchable artifact and ties every surface to the same ID:
system_error_logstable for 5xx responses, recovered panics, and streams that end in a terminal error event after a 2xx header. Plain 4xx stays in stdout only.chat_runs.request_id, so one ID leads to the full request.Approach:
response.InternalError(c, err)now takes the underlying error; every call site was migrated and a guard test rejectsInternalError(c, nil). The code and cause are stored on the request context instead ofgin.Context.Error, because otelgin would mark 4xx spans as errors.middleware.AccessLoggainedRoute,ErrorCode, andError(truncated to 4 KB), redacts credentials, and persists server-side failures through anErrorRecorder.errorDiagnosticlets an error type expose raw diagnostics;llm.UpstreamErrorreturns the upstream response body, which is stored indetailwith credentials masked and capped at 16 KB.Change type
Affected areas
Verification
Backend:
make -C backend lint— passedgo test ./...— passedgo build -tags nopostgres,noredis,nos3,noswagger,nomsgpack ./...— passedpnpm api:generateandpnpm api:check— regenerated and cleanAdded or extended tests:
AccessLog: persists only 5xx and failed streams, redacts the pathupstream request failedprefix de-duplicationTRUSTED_PROXIES, charset and length enforcedNot run:
pnpm --filter @deeix/web check(lint, typecheck, check:arch) was not run locally;apps/webhas no unit tests by design. CI covers it.Screenshots, API examples, or logs
UI screenshots for the new Errors tab and its detail sheet are attached.
{ "total": 1, "results": [ { "requestID": "e00dcc2b-04e9-4d55-91ba-e8da24cec0ba", "traceID": "ca05d3c6117a1fbf33d4ae78eef7bc69", "method": "POST", "route": "/api/v1/conversations/:id/messages/stream", "statusCode": 200, "errorCode": "upstream.unavailable", "cause": "upstream request failed: status=404 message=Not Found", "detail": "{ \"error\": { \"message\": \"Not Found\" } }", "latencyMS": 2583 } ] }Configuration, migration, and compatibility notes
system_error_logsis created byAutoMigrateat startup. A backend restart is required, and only failures that happen after the deployment are recorded.MessageResponsegainederrorRequestID. Swagger output (backend/docs) andpackages/api-contract/src/types.generated.tswere regenerated.GET /api/v1/admin/error-logs.errors; retention is then under admin control.errorRequestIDto"", so shared conversations never expose the owner's request IDs.Documentation
Note:
docs/rules/*is gitignored (.gitignore:169), so the rule updates for this change (backend code style §16.1, layering notes) are local-only and are not part of this diff. No other tracked documentation needed changes.Security and privacy
Reviewer notes:
X-Request-IDis now honored only when the request comes from a trusted proxy.key=valuepairs, URL credentials and sensitive query parameters). Masking is scoped to credentials only; everything else is kept verbatim so failures stay diagnosable.Checklist
.pycfiles,.envfiles, and local storage data are not committed.