JuliusBrussee/caveman · error

inline_data

Error message

inline_data: %w

What it means

The same custom UnmarshalJSON decodes the "inline_data" key into a geminiInlineData struct (mimeType/data). If "inline_data" is present but has the wrong shape — mistyped or missing mimeType/data fields, or a non-object value — the unmarshal error is wrapped as "inline_data: %w" and the part fails to decode.

Solutions

  1. Send inline_data as an object with string fields: {"inline_data": {"mimeType": "image/png", "data": "<base64>"}}.
  2. Verify the base64 payload is a JSON string, not raw bytes or an object.
  3. Check key naming consistency (snake_case inline_data vs camelCase inlineData) matches what the transform expects.

Example fix

// before
{"inline_data": "iVBORw0KGgo..."}
// after
{"inline_data": {"mimeType": "image/png", "data": "iVBORw0KGgo..."}}
Defensive patterns

Strategy: validation

Validate before calling

var probe struct {
    Parts []struct {
        InlineData *struct {
            MimeType string `json:"mimeType"`
            Data     string `json:"data"`
        } `json:"inline_data"`
    } `json:"parts"`
}
if err := json.Unmarshal(body, &probe); err != nil {
    return fmt.Errorf("inline_data must be {mimeType: string, data: string}")
}

Type guard

func validInlineData(part map[string]json.RawMessage) bool {
    v, ok := part["inline_data"]
    if !ok {
        return true
    }
    var in struct {
        MimeType string `json:"mimeType"`
        Data     string `json:"data"`
    }
    return json.Unmarshal(v, &in) == nil
}

Try / catch

var req geminiGenerateContent
if err := json.Unmarshal(body, &req); err != nil {
    if strings.Contains(err.Error(), "inline_data:") {
        return fmt.Errorf("inline_data must be an object with string mimeType and base64 data: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Unmarshaling a part containing "inline_data" that is not a valid {mimeType, data} object — e.g. "inline_data": "base64string" (bare string), or an object with "data" as a non-string.

Common situations: Clients using camelCase "inlineData" mixed with snake_case fields or nesting mismatch; attachments encoded without base64 string typing; fixtures ported from other multimodal providers.

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/8905fcf47a469b30. Report an issue: GitHub.

Appendix: source

Thrown at engine/pixel/transform_gemini.go:358

}

func (p *geminiPart) UnmarshalJSON(data []byte) error {
	var raw map[string]json.RawMessage
	if err := json.Unmarshal(data, &raw); err != nil {
		return err
	}
	p.Extra = raw
	if v, ok := raw["text"]; ok {
		var s string
		if err := json.Unmarshal(v, &s); err != nil {
			return fmt.Errorf("text: %w", err)
		}
		p.Text = &s
	}
	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

View on GitHub (pinned to 3ee70a1026)