{"record":{"id":"d069aab98f748da4","repo":"wavetermdev/waveterm","slug":"tool-input-is-not-an-object","errorCode":null,"errorMessage":"tool input is not an object","messagePattern":"tool input is not an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/aiusechat/anthropic/anthropic-backend.go","lineNumber":342,"sourceCode":"func (p *partialJSON) Bytes() []byte { return p.buf.Bytes() }\n\nfunc (p *partialJSON) FinalObject() (json.RawMessage, error) {\n\traw := p.buf.Bytes()\n\t// If empty, treat as \"{}\"\n\tif len(bytes.TrimSpace(raw)) == 0 {\n\t\treturn json.RawMessage(`{}`), nil\n\t}\n\t// The accumulated content should be a valid JSON object string; parse it.\n\tvar v interface{}\n\tif err := json.Unmarshal(raw, &v); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid accumulated tool input JSON: %w\", err)\n\t}\n\t// Ensure it's an object per Anthropic contract\n\tswitch v.(type) {\n\tcase map[string]interface{}:\n\t\treturn json.RawMessage(raw), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"tool input is not an object\")\n\t}\n}\n\n// sanitizeHostnameInError removes the Wave cloud hostname from error messages\nfunc sanitizeHostnameInError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\terrStr := err.Error()\n\tparsedURL, parseErr := url.Parse(uctypes.DefaultAIEndpoint)\n\tif parseErr == nil && parsedURL.Host != \"\" && strings.Contains(errStr, parsedURL.Host) {\n\t\terrStr = strings.ReplaceAll(errStr, uctypes.DefaultAIEndpoint, \"AI service\")\n\t\terrStr = strings.ReplaceAll(errStr, parsedURL.Host, \"host\")\n\t}\n\treturn fmt.Errorf(\"%s\", errStr)\n}\n\n// makeThinkingOpts creates thinking options based on level and max tokens","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/aiusechat/anthropic/anthropic-backend.go#L324-L360","documentation":"The Anthropic tool_use contract requires tool input to be a JSON object. FinalObject parses the accumulated stream successfully but the top-level value is not an object (e.g. an array, string, or number), so it refuses to return it as tool input. This is a defensive check against provider/tool-schema mismatches.","triggerScenarios":"A tool_use content block whose accumulated input_json_delta stream parses to valid JSON but with a non-object top-level type — e.g. the model emitted \"[1,2]\" or \"null\" as tool arguments.","commonSituations":"Tool schemas declared without a proper object input_schema (parameters as arrays or free types); a model misbehaving on a poorly-described tool; version changes in the Anthropic API emitting alternative structures.","solutions":["Fix the tool's input_schema so it declares type: object with defined properties.","Wrap the call and return a clear tool_result error back to the model so it can self-correct on the next turn.","Check Anthropic API/model version changes affecting tool input emission.","Log the raw accumulated JSON (json.RawMessage) to see what the model actually produced."],"exampleFix":"// before\nschema := map[string]any{\"type\": \"array\", \"items\": ...} // non-object input\n// after\nschema := map[string]any{\"type\": \"object\", \"properties\": map[string]any{\"items\": map[string]any{\"type\": \"array\"}} // object root","handlingStrategy":"type-guard","validationCode":"func isJSONObject(raw json.RawMessage) bool {\n    var m map[string]interface{}\n    return json.Unmarshal(raw, &m) == nil\n}","typeGuard":"func asObject(raw json.RawMessage) (map[string]interface{}, bool) {\n    var m map[string]interface{}\n    if err := json.Unmarshal(raw, &m); err != nil { return nil, false }\n    return m, true\n}","tryCatchPattern":"input, err := acc.FinalObject()\nif err != nil {\n    if strings.Contains(err.Error(), \"not an object\") {\n        return emitToolResultError(\"tool returned non-object input\") // let model retry\n    }\n    return err\n}","preventionTips":["Declare tool input_schema with type: object and explicit properties","Return a tool_result error to the model so it can correct its arguments next turn","Log the raw JSON to spot models repeatedly violating the schema","Test tool definitions against the Anthropic tool-use API before shipping"],"tags":["tool-use","json-schema","anthropic","streaming"],"backgroundTag":"tool-input-schema-mismatch","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}