Skip to content

Client tool continuation stays pending for 60 seconds after completion #2055

Closed

Description

@urjitc

Describe the bug
After submitting a client tool result with autoContinue: true, the continuation completes and the assistant response appears, but useAgentChat remains in the submitted state for about 60 seconds.

The server sends cf_agent_stream_pending but never sends cf_agent_stream_resuming. The client therefore ignores the matching done: true response until its pending timeout expires.

To Reproduce

  1. Create a client tool without a server-side execute function.
  2. Call the tool from a Think agent.
  3. Submit its result using addToolOutput.
  4. Wait for the continuation response.
  5. Observe that the response appears, but useAgentChat remains submitted for about 60 seconds.

This reproduces for both completed and dismissed tool interactions.

Expected behavior
The server should send cf_agent_stream_resuming when the continuation starts. useAgentChat should return to ready when the continuation finishes.

Screenshots
A recording or WebSocket trace can be provided.

Version:

  • agents@0.19.0
  • @cloudflare/think@0.15.0
  • ai@7.0.48
  • @ai-sdk/react@4.0.51

Additional context
The same WebSocket stays open throughout the request.

Observed sequence:

client: cf_agent_tool_result
client: cf_agent_stream_resume_request
server: cf_agent_stream_pending
server: cf_agent_use_chat_response { done: true }
server: cf_agent_chat_messages
cf_agent_stream_resuming is missing.

This appears to be the previously unconfirmed Gap 2 from #1586. PR #1608 fixed the earlier stream_resume_none race but not this case.

Activity

  1. self-assigned this
    on Aug 10, 2026
  2. ben-reitz commented on Aug 10, 2026

    @ben-reitz
    Contributor

    Hey @urjitc! Thanks for the report. I've done a quick investigation and confirmed that this reported frame sequence would cause useAgentChat to remain submitted until the 60-second pending timeout:

    cf_agent_stream_pending
    done: true
    # no cf_agent_stream_resuming
    

    The client currently does not retain the request ID from cf_agent_stream_pending, so it cannot use a matching terminal response to settle the continuation unless cf_agent_stream_resuming arrives first.


    However, I've not been able to reproduce the missing cf_agent_stream_resuming with stock @cloudflare/think. The relevant code in the reported versions is intended to emit:

    cf_agent_stream_pending → cf_agent_stream_resuming → done
    

    and our integration tests produce that sequence with matching request IDs.

    Could you share either a minimal reproduction or a complete WebSocket trace from immediately before addToolOutput through completion? The frame type, id, probeId, done, and timestamps are sufficient; message bodies can be redacted. It would also help to include:

    • Exact imports used for useAgentChat
    • Output from pnpm why agents @cloudflare/think (or equivalent)
    • Any custom lifecycle hooks or chat transport behavior

    There is a defensive client fix we could look at doing, but I'd also like to identify why the server notification was missing in your case.

  3. urjitc commented on Aug 10, 2026

    @urjitc
    Author

    @ben-reitz Thanks Ben, I isolated this to the sub agents and made a repro:

    https://github.com/urjitc/cloudflare-agents-2055-repro

    https://cloudflare-agents-2055-repro.chakrabortyurjit.workers.dev/?mode=sub-agent

    The reproduction uses the same Think implementation in two configurations:

    1. Direct Think: pending → resuming → done
    2. The same Think reached through a parent Agent as a sub-agent: pending → done

    The direct path returns useAgentChat to ready. The sub-agent path displays the completed assistant response but remains submitted until the pending timeout.

    The browser reproduction uses the real useAgentChat and addToolOutput flow. The only model customization is an overridden getModel() that returns a deterministic AI SDK model. It always requests the client tool and then returns Continuation completed.
    Exact imports:

    import { useAgentChat } from "@cloudflare/think/react";
    import { useAgent } from "agents/react";

    There are no custom lifecycle hooks, chat transports, or package patches. The parent agent is:

    export class ParentAgent extends Agent<Env> {}

    Dependency output:

    @cloudflare/think@0.15.0
    └── cloudflare-agents-2055-repro@0.0.0 (dependencies)
    
    agents@0.19.0
    ├─┬ @cloudflare/think@0.15.0
    │ └── cloudflare-agents-2055-repro@0.0.0 (dependencies)
    └── cloudflare-agents-2055-repro@0.0.0 (dependencies)
    
    Found 1 version of @cloudflare/think
    Found 1 version of agents
    

    To run the protocol reproduction:

    pnpm install
    pnpm reproduce

    It prints the relevant type, id, probeId, done, and continuation fields for both paths. The pending and terminal response IDs match in the failing sub-agent case, but cf_agent_stream_resuming is missing.

  4. urjitc commented on Aug 10, 2026

    @urjitc
    Author

    For context I ran into this issue when trying to add a AskUserQuestion tool in https://github.com/ThinkEx-OSS/thinkex where an agent can ask questions and the user gets nice ui to do so and sends the results back

  5. ben-reitz commented on Aug 26, 2026

    @ben-reitz
    Contributor

    This fix will go out in the next release

  6. O4epegb commented on Sep 1, 2026

    @O4epegb

    Hi, we upgraded to agents@0.22.0 and @cloudflare/think@0.17.0, but this issue still reproduces for client-side tool continuations through a sub-agent.
    After the user completes or dismisses a client-side tool interaction, we observe:

    cf_agent_stream_pending { id: "<request-id>" }
    cf_agent_use_chat_response {
      id: "<same-request-id>",
      done: true,
      continuation: true
    }
    # cf_agent_stream_resuming is missing
    

    The assistant’s response appears within a few seconds, but useAgentChat remains in the submitted state for approximately 60 seconds.
    This reproduces with autoContinueAfterToolResult: true for both completed and dismissed interactions. Setting it to false avoids the problem.

    It appears that PR #2090 does not cover every sub-agent continuation path maybe?

  7. O4epegb commented on Sep 14, 2026

    @O4epegb

    Update: we can no longer reproduce the 60-second pending state with autoContinueAfterToolResult: true. We retested both submitted and skipped client-side questionnaires through our sub-agent setup. Both now return to ready normally.

    Current versions: agents@0.22.0, @cloudflare/think@0.17.0, ai@7.0.98, and @ai-sdk/react@4.0.101.

    Our app has since migrated from AI SDK 6 to 7, while the Cloudflare package versions stayed unchanged. That may explain the improvement, but we haven’t isolated the SDK upgrade as the cause.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions