{"record":{"id":"cae56c7be474e2cc","repo":"JuliusBrussee/caveman","slug":"tools-w","errorCode":null,"errorMessage":"tools: %w","messagePattern":"tools: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/pixel/transform_gemini.go","lineNumber":256,"sourceCode":"\tif err := json.Unmarshal(body, &raw); err != nil {\n\t\treturn geminiGenerateContent{}, err\n\t}\n\treq := geminiGenerateContent{extra: raw}\n\tif v, ok := raw[\"contents\"]; ok {\n\t\tif err := json.Unmarshal(v, &req.Contents); err != nil {\n\t\t\treturn geminiGenerateContent{}, fmt.Errorf(\"contents: %w\", err)\n\t\t}\n\t}\n\tif v, ok := raw[\"systemInstruction\"]; ok && len(bytes.TrimSpace(v)) > 0 && !bytes.Equal(bytes.TrimSpace(v), []byte(\"null\")) {\n\t\tvar inst geminiInstruction\n\t\tif err := json.Unmarshal(v, &inst); err != nil {\n\t\t\treturn geminiGenerateContent{}, fmt.Errorf(\"systemInstruction: %w\", err)\n\t\t}\n\t\treq.SystemInstruction = &inst\n\t}\n\tif v, ok := raw[\"tools\"]; ok {\n\t\tif err := json.Unmarshal(v, &req.Tools); err != nil {\n\t\t\treturn geminiGenerateContent{}, fmt.Errorf(\"tools: %w\", err)\n\t\t}\n\t}\n\treturn req, nil\n}\n\nfunc geminiGlobalMediaResolution(raw map[string]json.RawMessage) (string, bool) {\n\tvar config json.RawMessage\n\tfor _, key := range []string{\"generationConfig\", \"generation_config\"} {\n\t\tif value, found := raw[key]; found {\n\t\t\tif config != nil {\n\t\t\t\treturn \"\", false\n\t\t\t}\n\t\t\tconfig = value\n\t\t}\n\t}\n\tif config == nil {\n\t\treturn \"default\", true\n\t}","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/pixel/transform_gemini.go#L238-L274","documentation":"parseGeminiGenerateContent optionally unmarshals \"tools\" into req.Tools. If the value exists but does not decode into the expected tool schema (wrong element types, missing/mistyped fields in function declarations), the unmarshal error is wrapped as \"tools: %w\" and TransformGemini fails.","triggerScenarios":"TransformGemini on a body where \"tools\" is present but malformed — e.g. a single object instead of an array of tools, or function declarations whose parameters schema has unexpected types.","commonSituations":"Tool definitions copied from OpenAI-style schemas that don't match Gemini's tool format; SDK version drift changing the tool JSON shape; fixtures with hand-edited tool arrays.","solutions":["Ensure \"tools\" is an array of Gemini tool objects (e.g. [{\"functionDeclarations\":[...]}]).","Check the wrapped error message for the exact field/type mismatch inside the tools array.","Convert tool schemas from other providers to Gemini's functionDeclarations format before sending."],"exampleFix":"// before\n{\"tools\": {\"functionDeclarations\": [...]}}\n// after\n{\"tools\": [{\"functionDeclarations\": [...]}]}","handlingStrategy":"validation","validationCode":"var probe struct {\n    Tools []map[string]json.RawMessage `json:\"tools\"`\n}\nif err := json.Unmarshal(body, &probe); err != nil {\n    return fmt.Errorf(\"tools must be an array of tool objects\")\n}","typeGuard":"func validTools(raw map[string]json.RawMessage) bool {\n    v, ok := raw[\"tools\"]\n    if !ok {\n        return true\n    }\n    var arr []map[string]json.RawMessage\n    return json.Unmarshal(v, &arr) == nil\n}","tryCatchPattern":"req, err := parseGeminiGenerateContent(body)\nif err != nil {\n    if strings.Contains(err.Error(), \"tools:\") {\n        var ue *json.UnmarshalTypeError\n        if errors.As(err, &ue) {\n            return fmt.Errorf(\"tools[%s] type mismatch: %w\", ue.Field, err)\n        }\n    }\n    return err\n}","preventionTips":["Use Gemini's tools array format: [{\"functionDeclarations\":[...]}].","Convert OpenAI-style tool schemas to Gemini's format before sending.","Unwrap the error with errors.As to find the exact field path inside the tools array."],"tags":["gemini","json","request-transform","schema"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}