{"record":{"id":"885c3a500d0938f2","repo":"zed-industries/zed","slug":"tool-input-was-not-fully-received","errorCode":null,"errorMessage":"tool input was not fully received","messagePattern":"tool input was not fully received","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/agent/src/thread.rs","lineNumber":4991,"sourceCode":"    #[cfg(any(test, feature = \"test-support\"))]\n    pub fn test() -> (ToolInputSender, Self) {\n        let (sender, input) = ToolInputSender::channel();\n        (sender, input.cast())\n    }\n\n    /// Wait for the final deserialized input, ignoring all partial updates.\n    /// Non-streaming tools can use this to wait until the whole input is available.\n    pub async fn recv(mut self) -> Result<T> {\n        while let Ok(value) = self.next().await {\n            match value {\n                ToolInputPayload::Full(value) => return Ok(value),\n                ToolInputPayload::Partial(_) => {}\n                ToolInputPayload::InvalidJson { error_message } => {\n                    return Err(anyhow!(error_message));\n                }\n            }\n        }\n        Err(anyhow!(\"tool input was not fully received\"))\n    }\n\n    pub async fn next(&mut self) -> Result<ToolInputPayload<T>> {\n        let value = self\n            .rx\n            .next()\n            .await\n            .ok_or_else(|| anyhow!(\"tool input was not fully received\"))?;\n\n        Ok(match value {\n            ToolInputPayload::Partial(payload) => ToolInputPayload::Partial(payload),\n            ToolInputPayload::Full(payload) => {\n                ToolInputPayload::Full(serde_json::from_value(payload)?)\n            }\n            ToolInputPayload::InvalidJson { error_message } => {\n                ToolInputPayload::InvalidJson { error_message }\n            }\n        })","sourceCodeStart":4973,"sourceCodeEnd":5009,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/agent/src/thread.rs#L4973-L5009","documentation":"ToolInputReceiver::recv awaits the fully deserialized tool input and returns this error when the payload stream ends without ever delivering ToolInputPayload::Full. The underlying mpsc channel closes when the producer (the streaming tool-call input) is dropped, which happens on turn cancellation, stream termination, or provider failure.","triggerScenarios":"The turn is cancelled while a streaming tool call is still emitting partial JSON; the provider ends the response stream mid tool-call without a complete input; or the producer task is dropped due to an upstream error, closing the channel with no Full payload sent.","commonSituations":"User presses stop during a long tool-argument stream; a network drop truncates the model stream; a provider bug closes the stream without finishing the tool call.","solutions":["Check whether the thread or turn was cancelled and treat the error as expected in that case","Retry the prompt or request if no cancellation happened; truncated streams are usually transient","If it recurs with one provider, switch providers or report it, since complete tool inputs should always arrive","Ensure tools surface this error instead of unwrap-ing the receiver"],"exampleFix":"// before\nlet input = receiver.recv().await?;\n\n// after\nlet input = match receiver.recv().await {\n    Ok(input) => input,\n    Err(err) if turn_was_cancelled() => return Ok(()), // stopping mid-stream is fine\n    Err(err) => {\n        log::warn!(\"tool input incomplete: {err}\");\n        return Err(err);\n    }\n};","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let input = match receiver.recv().await {\n    Ok(input) => input,\n    Err(err) if turn_was_cancelled() => return Ok(()), // expected on stop\n    Err(err) => {\n        log::warn!(\"tool input incomplete: {err}\");\n        return Err(err); // non-cancelled truncation: surface to the turn\n    }\n};","preventionTips":["Stop consuming the receiver once the turn is cancelled; a cancelled producer closes the channel without a Full payload","Consume the receiver to completion or drop it deliberately; never poll after the turn ended","Prefer recv() for non-streaming tools so partial fragments cannot be mistaken for complete input","Watch provider stream health; recurring truncation indicates a provider or network fault"],"tags":["rust","agent","tools","streaming","cancellation","channel"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}