{"record":{"id":"e296d8a917dc6f5c","repo":"Tencent/WeKnora","slug":"failed-to-marshal-exa-request-w","errorCode":null,"errorMessage":"failed to marshal Exa request: %w","messagePattern":"failed to marshal Exa request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/exa.go","lineNumber":85,"sourceCode":"\t\treturn nil, fmt.Errorf(\"query is empty\")\n\t}\n\tif maxResults <= 0 {\n\t\tmaxResults = defaultExaResults\n\t}\n\tif maxResults > maxExaResults {\n\t\tmaxResults = maxExaResults\n\t}\n\n\tbodyBytes, err := json.Marshal(exaSearchRequest{\n\t\tQuery:      query,\n\t\tNumResults: maxResults,\n\t\tContents: exaContents{\n\t\t\tHighlights: true,\n\t\t\tText:       p.includeText,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to marshal Exa request: %w\", err)\n\t}\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, p.baseURL, bytes.NewReader(bodyBytes))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create Exa request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"x-api-key\", p.apiKey)\n\n\tlogger.Infof(ctx, \"[WebSearch][Exa] query=%q maxResults=%d url=%s\", query, maxResults, p.baseURL)\n\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to execute Exa request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxExaResponseBytes))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read Exa response: %w\", err)\n\t}","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/exa.go#L67-L103","documentation":"Wraps errors from json.Marshal when serializing the Exa search request payload (query, result count, contents options). With a plain struct of string/bool/int fields this is nearly impossible at runtime and signals a programming or struct-definition problem rather than an environmental one.","triggerScenarios":"Calling Search when the exaRequest struct cannot be marshaled — practically only if the struct gains unsupported field types (e.g. chan, func, or a custom Marshaler that errors).","commonSituations":"After a code change adding a field with an unsupported type or a custom json.Marshaler that returns an error; essentially never in production with the current fixed schema.","solutions":["Review the request struct for unsupported field types or faulty custom MarshalJSON methods","Revert recent changes to the exaRequest struct","If truly impossible, treat as an internal invariant bug and alert rather than retry","Check the unwrapped json.UnsupportedTypeError for the offending field"],"exampleFix":"// before\nContents: exaContents{ Channel: make(chan int) }, // unmarshalable field\n// after\nContents: exaContents{ Highlights: true, Text: p.includeText },","handlingStrategy":"validation","validationCode":"func validateExaRequestFields(r exaRequest) error {\n    if r.NumResults < 0 {\n        return errors.New(\"numResults must be non-negative\")\n    }\n    return nil // current struct is JSON-safe; marshal errors indicate a struct change\n}","typeGuard":null,"tryCatchPattern":"results, err := provider.Search(ctx, query, 10, false)\nif err != nil && strings.Contains(err.Error(), \"failed to marshal Exa request\") {\n    return nil, fmt.Errorf(\"internal bug in exa request construction: %w\", err)\n}","preventionTips":["Keep the request struct to JSON-safe types (string, bool, int, slices)","Add a unit test that marshals a fully populated exaRequest","Avoid custom MarshalJSON unless necessary; if present, unit-test its error paths"],"tags":["json","marshaling","go","internal"],"backgroundTag":"json-marshal-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}