{"record":{"id":"e3ad772ac2ecb036","repo":"aaif-goose/goose","slug":"missing-thinking-signature","errorCode":null,"errorMessage":"Missing thinking signature","messagePattern":"Missing thinking signature","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-provider-types/src/formats/snowflake.rs","lineNumber":272,"sourceCode":"                    .to_string();\n\n                let input = content\n                    .get(\"input\")\n                    .ok_or_else(|| anyhow!(\"Missing tool input\"))?\n                    .clone();\n\n                let tool_call = CallToolRequestParams::new(name).with_arguments(object(input));\n                message = message.with_tool_request(id, Ok(tool_call));\n            }\n            Some(\"thinking\") => {\n                let thinking = content\n                    .get(\"thinking\")\n                    .and_then(|t| t.as_str())\n                    .ok_or_else(|| anyhow!(\"Missing thinking content\"))?;\n                let signature = content\n                    .get(\"signature\")\n                    .and_then(|s| s.as_str())\n                    .ok_or_else(|| anyhow!(\"Missing thinking signature\"))?;\n                message = message.with_thinking(thinking, signature);\n            }\n            Some(\"redacted_thinking\") => {\n                let data = content\n                    .get(\"data\")\n                    .and_then(|d| d.as_str())\n                    .ok_or_else(|| anyhow!(\"Missing redacted_thinking data\"))?;\n                message = message.with_redacted_thinking(data);\n            }\n            _ => {\n                // Ignore unrecognized content types\n            }\n        }\n    }\n\n    Ok(message)\n}\n","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-provider-types/src/formats/snowflake.rs#L254-L290","documentation":"The second required field of a Snowflake Cortex thinking block: signature, the cryptographic proof string that lets the API accept the reasoning on the next turn. If a type \"thinking\" block has thinking text but its signature key is missing or not a string, response_to_message fails with 'Missing thinking signature'.","triggerScenarios":"A thinking block like {\"type\":\"thinking\",\"thinking\":\"...\"} with no signature — typical of gateways that strip signatures for size, of fixtures that only model the text, or of schema variants where the signature lives one level up.","commonSituations":"Recording/replaying Cortex traffic with signatures redacted (they look like secrets and get scrubbed); prompt/response transformation layers trimming long fields; API schema drift moving signature elsewhere.","solutions":["Confirm the raw block includes a string \"signature\" field","If signatures are being scrubbed by a logging/privacy layer, exclude thinking blocks from scrubbing — the signature is required for multi-turn reasoning","Fix fixtures to include a signature value (any non-empty string for tests)","For endpoints without signature support, skip such blocks rather than failing the whole response"],"exampleFix":"// before\nlet signature = content\n    .get(\"signature\")\n    .and_then(|s| s.as_str())\n    .ok_or_else(|| anyhow!(\"Missing thinking signature\"))?;\n\n// after - degrade to a text block instead of failing the conversion\nlet signature = match content.get(\"signature\").and_then(|s| s.as_str()) {\n    Some(s) => s,\n    None => {\n        tracing::warn!(\"thinking block without signature; keeping text only\");\n        message = message.with_text(thinking.to_string());\n        continue;\n    }\n};","handlingStrategy":"validation","validationCode":"for block in content_list {\n    if block.get(\"type\").and_then(|t| t.as_str()) == Some(\"thinking\")\n        && block.get(\"signature\").and_then(|s| s.as_str()).is_none()\n    {\n        anyhow::bail!(\"thinking block without string signature: {block}\");\n    }\n}","typeGuard":"fn thinking_block_has_signature(block: &serde_json::Value) -> bool {\n    if block.get(\"type\").and_then(|t| t.as_str()) != Some(\"thinking\") {\n        return true;\n    }\n    block.get(\"signature\").and_then(|v| v.as_str()).is_some()\n}","tryCatchPattern":"match response_to_message(&response) {\n    Ok(msg) => msg,\n    Err(err) if err.to_string().contains(\"Missing thinking signature\") => {\n        tracing::warn!(\"thinking without signature cannot round-trip; dropping: {err}\");\n        Message::assistant()\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Exclude signature fields from redaction/scrubbing pipelines — multi-turn reasoning fails without them","Avoid response-transforming proxies that trim long fields","Test the full tool-use + reasoning round trip, not just single responses"],"tags":["rust","snowflake","cortex","reasoning","parsing"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}