JuliusBrussee/caveman · error

functionCall

Error message

functionCall: %w

What it means

During Gemini part JSON unmarshaling, the 'functionCall' key could not be decoded into geminiFunctionCall. The wrapper error names the field so callers can pinpoint which part of the Gemini payload is malformed. It signals an invalid functionCall object in the request.

Solutions

  1. Read the wrapped inner error to identify the exact type mismatch inside functionCall
  2. Ensure functionCall is an object with the fields geminiFunctionCall expects (e.g. name, args)
  3. Dump the raw JSON of the part to locate the malformed structure
  4. Fix the payload producer or converter to emit valid Gemini functionCall objects

Example fix

// before
parts: [{functionCall: 'myTool'}]
// after
parts: [{functionCall: {name: 'myTool', args: {q: 'x'}}}]
Defensive patterns

Strategy: validation

Validate before calling

func validFunctionCall(p map[string]any) bool {
  fc, ok := p["functionCall"].(map[string]any)
  return !ok || (fc["name"] != nil && (fc["args"] == nil || isObject(fc["args"])))
}

Type guard

func isObject(v any) bool { _, ok := v.(map[string]any); return ok }

Try / catch

if err := json.Unmarshal(data, &part); err != nil {
  if strings.HasPrefix(err.Error(), "functionCall:") {
    // surface the raw functionCall JSON for inspection
  }
}

Prevention

When it happens

Trigger: UnmarshalJSON on a Gemini part where raw["functionCall"] exists but its JSON does not match geminiFunctionCall (e.g. missing name, args not an object, wrong types).

Common situations: Clients emitting functionCall as a string or array; model-output relayed verbatim with unexpected shape; schema drift between Gemini API versions.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/23860faf1154ab92. Report an issue: GitHub.

Appendix: source

Thrown at engine/pixel/transform_gemini.go:372

	}
	if v, ok := raw["inline_data"]; ok {
		var inline geminiInlineData
		if err := json.Unmarshal(v, &inline); err != nil {
			return fmt.Errorf("inline_data: %w", err)
		}
		p.InlineData = &inline
	}
	if v, ok := raw["functionResponse"]; ok {
		var fr geminiFunctionResponse
		if err := json.Unmarshal(v, &fr); err != nil {
			return fmt.Errorf("functionResponse: %w", err)
		}
		p.FunctionResponse = &fr
	}
	if v, ok := raw["functionCall"]; ok {
		var fc geminiFunctionCall
		if err := json.Unmarshal(v, &fc); err != nil {
			return fmt.Errorf("functionCall: %w", err)
		}
		p.FunctionCall = &fc
	}
	return nil
}

func (p geminiPart) MarshalJSON() ([]byte, error) {
	raw := make(map[string]json.RawMessage, len(p.Extra)+4)
	for k, v := range p.Extra {
		raw[k] = v
	}
	if p.Text != nil {
		b, err := json.Marshal(*p.Text)
		if err != nil {
			return nil, err
		}
		raw["text"] = b
	} else {

View on GitHub (pinned to 3ee70a1026)