{"record":{"id":"ec7b6632a9ee5387","repo":"ory/hydra","slug":"server-error-ec7b66","errorCode":"server_error","errorMessage":"{\"error\":\"server_error\",\"error_description\":\"%s\"}","messagePattern":"\\{\"error\":\"server_error\",\"error_description\":\"%s\"\\}","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"fosite/access_error.go","lineNumber":33,"sourceCode":"\tf.writeJsonError(ctx, rw, req, err)\n}\n\nfunc (f *Fosite) writeJsonError(ctx context.Context, rw http.ResponseWriter, requester Requester, err error) {\n\trw.Header().Set(\"Content-Type\", \"application/json;charset=UTF-8\")\n\trw.Header().Set(\"Cache-Control\", \"no-store\")\n\trw.Header().Set(\"Pragma\", \"no-cache\")\n\n\trfcerr := ErrorToRFC6749Error(err).WithLegacyFormat(f.Config.GetUseLegacyErrorFormat(ctx)).WithExposeDebug(f.Config.GetSendDebugMessagesToClients(ctx))\n\n\tif requester != nil {\n\t\trfcerr = rfcerr.WithLocalizer(f.Config.GetMessageCatalog(ctx), getLangFromRequester(requester))\n\t}\n\n\tjs, err := json.Marshal(rfcerr)\n\tif err != nil {\n\t\tif f.Config.GetSendDebugMessagesToClients(ctx) {\n\t\t\terrorMessage := EscapeJSONString(err.Error())\n\t\t\thttp.Error(rw, fmt.Sprintf(`{\"error\":\"server_error\",\"error_description\":\"%s\"}`, errorMessage), http.StatusInternalServerError)\n\t\t} else {\n\t\t\thttp.Error(rw, `{\"error\":\"server_error\"}`, http.StatusInternalServerError)\n\t\t}\n\t\treturn\n\t}\n\n\trw.WriteHeader(rfcerr.CodeField)\n\t// ignoring the error because the connection is broken when it happens\n\t_, _ = rw.Write(js)\n}\n","sourceCodeStart":15,"sourceCodeEnd":44,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/fosite/access_error.go#L15-L44","documentation":"In fosite's writeJsonError, if json.Marshal of the RFC6749 error object itself fails, the handler cannot emit the normal JSON error body. When GetSendDebugMessagesToClients is enabled, it returns a hand-built {\"error\":\"server_error\",\"error_description\":\"<marshal error>\"} with status 500. This is a last-resort path; Marshal of fosite's error struct essentially never fails, so encountering it signals a deeply unexpected internal condition or a customized Config/Error implementation with unmarshalable fields.","triggerScenarios":"json.Marshal(rfcerr) returns an error inside writeJsonError (fosite/access_error.go:33), reached via WriteAccessError or WriteIntrospectionError — e.g. a custom RichError/args value containing channels, funcs, or cyclic references placed in the error's internal fields, or a custom Config whose debug-message wiring injected unsupported data.","commonSituations":"Custom error types attached to requests by custom grant handlers carrying non-serializable context; debugging enabled (SendDebugMessagesToClients: true) so the raw marshal error leaks to clients; embedding requests via form round-trips (RestoreRequest) that corrupted error metadata.","solutions":["Audit the error objects your handlers attach (WithLocalError, custom args) for non-JSON-serializable values (channels, funcs, cycles).","Set SendDebugMessagesToClients to false in production so marshal failures don't leak internals (you'd then get the plain {\"error\":\"server_error\"} variant).","Upgrade fosite — past releases fixed marshal edge cases around error serialization.","Reproduce with a wrapper: temporarily marshal the same rfcerr in a test to capture the underlying error message."],"exampleFix":"// before (custom grant handler attaching unserializable data)\nctx = fosite.WithRequestContext(ctx, myReq.WithSession(session{Done: make(chan int)}))\n// after\nctx = fosite.WithRequestContext(ctx, myReq.WithSession(session{UserID: userID}))","handlingStrategy":"try-catch","validationCode":"// Pre-flight: ensure your error/session values serialize.\nif _, err := json.Marshal(myCustomErrorArgs); err != nil {\n\tlog.Printf(\"custom error payload not serializable: %v\", err)\n}","typeGuard":"func jsonSafe(v any) bool {\n\t_, err := json.Marshal(v)\n\treturn err == nil\n}","tryCatchPattern":"// Library-internal path; guard at the handler boundary:\nif err := nextHandler(ctx, rw, req); err != nil {\n\tif jsonSafe(err) {\n\t\tlog.Printf(\"request error: %v\", err)\n\t}\n\t// serialize your own RFC6749 body instead of relying on fosite's fallback\n\twriteRFC6749Error(rw, err, http.StatusInternalServerError)\n}","preventionTips":["Never attach channels, funcs, mutexes, or contexts to fosite error args","Set SendDebugMessagesToClients=false in production to avoid leaking marshal errors","Add unit tests marshaling every custom error type your handlers produce","Keep fosite updated — internal marshal paths should be unreachable"],"tags":["oauth2","fosite","json"],"backgroundTag":"json-marshal-failed","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}