JuliusBrussee/caveman · error
systemInstruction
Error message
systemInstruction: %w
What it means
parseGeminiGenerateContent optionally unmarshals "systemInstruction" into a geminiInstruction when present, non-empty, and not literal null. If the value has the wrong shape (e.g. a bare string, or an object with unexpected field types), the unmarshal error is wrapped as "systemInstruction: %w" and TransformGemini fails.
Solutions
- Send systemInstruction as an object: {"parts":[{"text":"..."}]} rather than a bare string.
- Read the wrapped error to identify which field type did not match.
- If your SDK emits the string form, transform it to the parts-object form before calling TransformGemini.
Example fix
// before
{"systemInstruction": "You are helpful."}
// after
{"systemInstruction": {"parts": [{"text": "You are helpful."}]}} Defensive patterns
Strategy: validation
Validate before calling
var probe struct {
SystemInstruction *struct {
Parts []map[string]json.RawMessage `json:"parts"`
} `json:"systemInstruction"`
}
if err := json.Unmarshal(body, &probe); err != nil {
return fmt.Errorf("systemInstruction must be {parts:[{text:...}]}")
} Type guard
func validSystemInstruction(raw map[string]json.RawMessage) bool {
v, ok := raw["systemInstruction"]
if !ok || len(bytes.TrimSpace(v)) == 0 || bytes.Equal(bytes.TrimSpace(v), []byte("null")) {
return true
}
var inst map[string]json.RawMessage
return json.Unmarshal(v, &inst) == nil && inst["parts"] != nil
} Try / catch
req, err := parseGeminiGenerateContent(body)
if err != nil {
if strings.Contains(err.Error(), "systemInstruction:") {
return fmt.Errorf("send systemInstruction as {parts:[{text:...}]}, not a bare string")
}
return err
} Prevention
- Always send systemInstruction as a parts-object, never a bare string.
- Check which Gemini API revision your client targets; normalize the field before transforming.
- Unwrap errors.As(*json.UnmarshalTypeError) to see the mismatched field name.
When it happens
Trigger: TransformGemini on a body where "systemInstruction" is present but not a valid instruction object — e.g. a plain string (some Gemini API variants accept string, this struct does not) or an object whose parts field has the wrong type.
Common situations: Clients built against a different Gemini API revision that sends systemInstruction as a string; templates that inject a raw string instead of {parts:[{text:...}]}; migrated prompts from other 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/fb06d5ca6a5b40a2.
Report an issue: GitHub.
Appendix: source
Thrown at engine/pixel/transform_gemini.go:250
def.EmitRecoverable = opts.EmitRecoverable
return def
}
func parseGeminiGenerateContent(body []byte) (geminiGenerateContent, error) {
var raw map[string]json.RawMessage
if err := json.Unmarshal(body, &raw); err != nil {
return geminiGenerateContent{}, err
}
req := geminiGenerateContent{extra: raw}
if v, ok := raw["contents"]; ok {
if err := json.Unmarshal(v, &req.Contents); err != nil {
return geminiGenerateContent{}, fmt.Errorf("contents: %w", err)
}
}
if v, ok := raw["systemInstruction"]; ok && len(bytes.TrimSpace(v)) > 0 && !bytes.Equal(bytes.TrimSpace(v), []byte("null")) {
var inst geminiInstruction
if err := json.Unmarshal(v, &inst); err != nil {
return geminiGenerateContent{}, fmt.Errorf("systemInstruction: %w", err)
}
req.SystemInstruction = &inst
}
if v, ok := raw["tools"]; ok {
if err := json.Unmarshal(v, &req.Tools); err != nil {
return geminiGenerateContent{}, fmt.Errorf("tools: %w", err)
}
}
return req, nil
}
func geminiGlobalMediaResolution(raw map[string]json.RawMessage) (string, bool) {
var config json.RawMessage
for _, key := range []string{"generationConfig", "generation_config"} {
if value, found := raw[key]; found {
if config != nil {
return "", false
}View on GitHub (pinned to 3ee70a1026)