zed-industries/zed · error

tool input was not fully received

Error message

tool input was not fully received

What it means

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.

Source

Thrown at crates/agent/src/thread.rs:4991

    #[cfg(any(test, feature = "test-support"))]
    pub fn test() -> (ToolInputSender, Self) {
        let (sender, input) = ToolInputSender::channel();
        (sender, input.cast())
    }

    /// Wait for the final deserialized input, ignoring all partial updates.
    /// Non-streaming tools can use this to wait until the whole input is available.
    pub async fn recv(mut self) -> Result<T> {
        while let Ok(value) = self.next().await {
            match value {
                ToolInputPayload::Full(value) => return Ok(value),
                ToolInputPayload::Partial(_) => {}
                ToolInputPayload::InvalidJson { error_message } => {
                    return Err(anyhow!(error_message));
                }
            }
        }
        Err(anyhow!("tool input was not fully received"))
    }

    pub async fn next(&mut self) -> Result<ToolInputPayload<T>> {
        let value = self
            .rx
            .next()
            .await
            .ok_or_else(|| anyhow!("tool input was not fully received"))?;

        Ok(match value {
            ToolInputPayload::Partial(payload) => ToolInputPayload::Partial(payload),
            ToolInputPayload::Full(payload) => {
                ToolInputPayload::Full(serde_json::from_value(payload)?)
            }
            ToolInputPayload::InvalidJson { error_message } => {
                ToolInputPayload::InvalidJson { error_message }
            }
        })

View on GitHub (pinned to bc538def45)

Solutions

  1. Check whether the thread or turn was cancelled and treat the error as expected in that case
  2. Retry the prompt or request if no cancellation happened; truncated streams are usually transient
  3. If it recurs with one provider, switch providers or report it, since complete tool inputs should always arrive
  4. Ensure tools surface this error instead of unwrap-ing the receiver

Example fix

// before
let input = receiver.recv().await?;

// after
let input = match receiver.recv().await {
    Ok(input) => input,
    Err(err) if turn_was_cancelled() => return Ok(()), // stopping mid-stream is fine
    Err(err) => {
        log::warn!("tool input incomplete: {err}");
        return Err(err);
    }
};
Defensive patterns

Strategy: try-catch

Try / catch

let input = match receiver.recv().await {
    Ok(input) => input,
    Err(err) if turn_was_cancelled() => return Ok(()), // expected on stop
    Err(err) => {
        log::warn!("tool input incomplete: {err}");
        return Err(err); // non-cancelled truncation: surface to the turn
    }
};

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/885c3a500d0938f2. Report an issue: GitHub.