{"record":{"id":"80e6566228cc9591","repo":"plandex-ai/plandex","slug":"error-marshalling-response-80e656","errorCode":null,"errorMessage":"Error marshalling response","messagePattern":"Error marshalling response","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/server/handlers/err_helper.go","lineNumber":16,"sourceCode":"package handlers\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"net/http\"\n\n\tshared \"plandex-shared\"\n)\n\nfunc writeApiError(w http.ResponseWriter, apiErr shared.ApiError) {\n\tbytes, err := json.Marshal(apiErr)\n\tif err != nil {\n\t\tlog.Printf(\"Error marshalling response: %v\\n\", err)\n\t\t// If marshalling fails, fall back to a simpler error message\n\t\thttp.Error(w, \"Error marshalling response\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tlog.Printf(\"API Error: %v\\n\", apiErr.Msg)\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(apiErr.Status)\n\n\t_, writeErr := w.Write(bytes)\n\tif writeErr != nil {\n\t\tlog.Printf(\"Error writing response: %v\\n\", writeErr)\n\t}\n}\n","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/err_helper.go#L1-L30","documentation":"writeApiError is the shared helper for returning JSON ApiError bodies from HTTP handlers. Before writing the body it marshals the ApiError struct with encoding/json; if that marshal itself fails it logs 'Error marshalling response' and falls back to a plain-text 500 response. This is a defensive last-resort path: an ApiError with non-marshalable field values (e.g. unsupported types like channels, funcs, or invalid numbers such as NaN in custom fields) cannot be serialized.","triggerScenarios":"A handler (CreateAccountHandler, execAuthenticate, InviteUserHandler, ListPendingInvitesHandler, ListAcceptedInvitesHandler, ListAllInvitesHandler) constructs a shared.ApiError whose payload contains a value encoding/json cannot marshal — e.g. a channel, func, complex number, or cyclic data structure placed in an extension/payload field.","commonSituations":"Developers extending ApiError with a custom Data/Payload field and stuffing runtime objects into it; refactors that change an ID field from string to a struct containing unexported or unsupported types; NaN/Inf floats leaking into error details from computed metrics.","solutions":["Inspect the log line 'Error marshalling response: %v' to see the json.UnsupportedTypeError and identify the offending field","Ensure every field of shared.ApiError (and any embedded payload) contains only JSON-serializable types (strings, numbers, bools, slices, maps, structs)","Convert custom payload values to strings or a typed DTO before constructing the ApiError","If the ApiError is built from arbitrary data, sanitize it (e.g. re-marshal through json.RawMessage or a generic map[string]interface{}) before passing to writeApiError"],"exampleFix":"// before\napiErr := shared.ApiError{Msg: \"failed\", Details: someChan}\nwriteApiError(w, apiErr)\n// after\napiErr := shared.ApiError{Msg: \"failed\", Details: fmt.Sprintf(\"%v\", someChan)}\nwriteApiError(w, apiErr)","handlingStrategy":"fallback","validationCode":"// Validate the ApiError is JSON-safe before sending\nfunc isJSONSafe(v interface{}) bool {\n    _, err := json.Marshal(v)\n    return err == nil\n}\nif !isJSONSafe(apiErr) {\n    apiErr = shared.ApiError{Msg: apiErr.Msg} // drop unsafe payload\n}","typeGuard":"func safeApiError(e shared.ApiError) shared.ApiError {\n    b, err := json.Marshal(e)\n    if err != nil || !json.Valid(b) {\n        return shared.ApiError{Msg: \"internal error\"}\n    }\n    return e\n}","tryCatchPattern":"// Go has no try/catch; wrap the write and check marshal error at the call site\nif b, err := json.Marshal(apiErr); err != nil {\n    log.Printf(\"api error not marshalable: %v\", err)\n    http.Error(w, http.StatusText(http.StatusInternalServerError), 500)\n    return\n}","preventionTips":["Keep ApiError fields limited to JSON-native types","Never embed channels, funcs, or complex numbers in error payloads","Unit-test writeApiError with every ApiError variant your handlers produce","Sanitize dynamic payload data before attaching it to errors"],"tags":["go","json","http","serialization"],"backgroundTag":"json-marshal-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}