{"record":{"id":"9b9b1083523f84af","repo":"vxcontrol/pentagi","slug":"failed-to-marshal-request-body-w","errorCode":null,"errorMessage":"failed to marshal request body: %w","messagePattern":"failed to marshal request body: %w","errorType":"exception","errorClass":"FatalError","httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/searchers/perplexity.go","lineNumber":168,"sourceCode":"\t}\n\n\t// Forming the request\n\treqPayload := CompletionRequest{\n\t\tMessages:               messages,\n\t\tModel:                  p.model(),\n\t\tSearchContextSize:      p.contextSize(),\n\t\tMaxTokens:              p.maxTokens(),\n\t\tTemperature:            p.temperature(),\n\t\tTopP:                   p.topP(),\n\t\tReturnImages:           false,\n\t\tReturnRelatedQuestions: false,\n\t\tStream:                 false,\n\t}\n\n\t// Serializing the request\n\treqBody, err := json.Marshal(reqPayload)\n\tif err != nil {\n\t\treturn \"\", Fatal(fmt.Errorf(\"failed to marshal request body: %w\", err))\n\t}\n\n\t// Creating HTTP request\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, perplexityURL, bytes.NewBuffer(reqBody))\n\tif err != nil {\n\t\treturn \"\", Fatal(fmt.Errorf(\"failed to create request: %w\", err))\n\t}\n\n\t// Setting request headers\n\treq.Header.Set(\"Authorization\", \"Bearer \"+p.apiKey())\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// Sending the request\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn \"\", Retryable(fmt.Errorf(\"failed to send request: %w\", err), 0)\n\t}\n\tdefer resp.Body.Close()","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/searchers/perplexity.go#L150-L186","documentation":"Before sending, `search` serializes the CompletionRequest payload with `json.Marshal`. Failure is wrapped as Fatal (backend/pkg/tools/searchers/perplexity.go:168). With a fully in-code struct this is nearly impossible at runtime — it only happens if the payload contains values json cannot encode, such as NaN/Inf floats or a channel/func field added to the struct.","triggerScenarios":"`json.Marshal(reqPayload)` errors because a field value is not JSON-encodable — e.g. a NaN/±Inf float leaking into Temperature/TopP from a misparsed config, or a custom build that added an unsupported field type to CompletionRequest.","commonSituations":"Temperature/TopP env var parsed via strconv/regex producing NaN; custom fork patched CompletionRequest with a non-serializable field; json tag/type mismatch after a struct refactor.","solutions":["Check the wrapped marshal error — it names the offending field/type","Validate config-derived floats with math.IsNaN/IsInf before building reqPayload and fall back to defaults","Revert recent changes to CompletionRequest; keep only JSON-serializable field types with correct tags","Log reqPayload fields at debug level to spot the bad value"],"exampleFix":"// before\ntemp := parseTemperature(env) // NaN possible\n// after\ntemp := parseTemperature(env)\nif math.IsNaN(temp) || math.IsInf(temp, 0) {\n    temp = defaultTemperature\n}","handlingStrategy":"validation","validationCode":"// validate numeric config before building the payload\nfor _, v := range []float64{temp, topP} {\n    if math.IsNaN(v) || math.IsInf(v, 0) {\n        return fmt.Errorf(\"non-finite value in Perplexity config\")\n    }\n}","typeGuard":null,"tryCatchPattern":"_, err := engine.Handle(ctx, req)\nif err != nil && searchers.IsFatal(err) && strings.Contains(err.Error(), \"failed to marshal request body\") {\n    // inspect logged payload fields for NaN/Inf or non-serializable values\n}","preventionTips":["Clamp/sanitize Temperature, TopP, MaxTokens parsed from env to sane finite ranges","Keep CompletionRequest fields JSON-encodable with correct tags","Add a debug log of the request payload before marshal in development","Test config parsing edge cases in unit tests (config_test.go)"],"tags":["json","serialization","configuration"],"backgroundTag":"json-marshal-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}