{"record":{"id":"8f5d3296396ca1b9","repo":"openfaas/faas","slug":"error-writing-response-after-adding-metrics","errorCode":null,"errorMessage":"Error writing response after adding metrics","messagePattern":"Error writing response after adding metrics","errorType":"http","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"gateway/metrics/add_metrics.go","lineNumber":73,"sourceCode":"\n\t\tif len(functions) > 0 {\n\n\t\t\tns := functions[0].Namespace\n\t\t\tq := fmt.Sprintf(`sum(gateway_function_invocation_total{function_name=~\".*.%s\"}) by (function_name)`, ns)\n\t\t\t// Restrict query results to only function names matching namespace suffix.\n\n\t\t\tresults, err := prometheusQuery.Fetch(url.QueryEscape(q))\n\t\t\tif err != nil {\n\t\t\t\t// log the error but continue, the mixIn will correctly handle the empty results.\n\t\t\t\tlog.Printf(\"Error querying Prometheus: %s\\n\", err.Error())\n\t\t\t}\n\t\t\tmixIn(&functions, results)\n\t\t}\n\n\t\tbytesOut, err := json.Marshal(functions)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error serializing functions: %s\", err)\n\t\t\thttp.Error(w, \"Error writing response after adding metrics\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\tw.WriteHeader(http.StatusOK)\n\t\tw.Write(bytesOut)\n\t}\n}\n\nfunc mixIn(functions *[]types.FunctionStatus, metrics *VectorQueryResponse) {\n\n\tif functions == nil {\n\t\treturn\n\t}\n\n\tfor i, function := range *functions {\n\t\tfor _, v := range metrics.Data.Result {\n","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/openfaas/faas/blob/8d803bf9e2655aec6a60fd0e25b392839f96af95/gateway/metrics/add_metrics.go#L55-L91","documentation":"After enriching the function list with Prometheus data, AddMetricsHandler re-serializes it with json.Marshal; a marshal failure returns 500 'Error writing response after adding metrics'. Because the data was just produced by unmarshaling provider JSON into plain structs (strings and numbers only), every field is marshal-safe and this branch is effectively unreachable defensive code — hitting it indicates corruption or modified code.","triggerScenarios":"Not reproducible through normal requests: []types.FunctionStatus cannot carry non-marshalable values after a round-trip through json.Unmarshal. Only a patched or forked gateway adding a chan/func/NaN field to FunctionStatus, or altering mixIn, could trigger it.","commonSituations":"Custom forks extending FunctionStatus or mixIn with unsupported types; memory corruption; reports that almost always turn out to be a nearby, different error.","solutions":["Treat occurrences as a bug: capture the gateway log line 'Error serializing functions: ...' which names the marshal error","Check for local patches or forks modifying types.FunctionStatus or mixIn","Retry the request once to rule out transient corruption","Report upstream with the log output if it reproduces on an unmodified gateway"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := client.Get(gateway + \"/system/functions\")\nif err != nil || resp.StatusCode == http.StatusInternalServerError {\n    // practically unreachable server branch: retry once, and capture the\n    // gateway log line 'Error serializing functions' for a bug report\n    time.Sleep(backoff)\n    continue\n}","preventionTips":["If running a fork, keep FunctionStatus fields JSON-marshalable","Capture gateway logs whenever this 500 appears — the real cause is logged one line above"],"tags":["json","serialization","defensive","metrics"],"backgroundTag":null,"analyzedSha":"8d803bf9e2655aec6a60fd0e25b392839f96af95","analyzedAt":"2026-08-16T00:12:29.759Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}