Skip to content

callTool does not retry on transient network errors (socket closed during concurrent requests) #114

Open

Description

@icebear0828

Description

When multiple callTool() requests run concurrently (e.g. generating variants with Pro model), the Stitch server occasionally closes the underlying socket. The SDK surfaces this as an unrecoverable TypeError: fetch failed to the caller, even though the operation would succeed on a simple retry with a fresh connection.

Steps to reproduce

const client = new StitchToolClient({ apiKey: "..." });
const project = await client.callTool("create_project", { title: "test" });
const projectId = project.name.replace("projects/", "");

const screen = await client.callTool("generate_screen_from_text", {
  projectId,
  prompt: "a dashboard",
  modelId: "GEMINI_3_PRO",
});
const screenId = screen.outputComponents[0].design.screens[0].id;

// Fire 3 concurrent variant requests — one or more will fail
await Promise.allSettled([
  client.callTool("generate_variants", {
    projectId,
    selectedScreenIds: [screenId],
    prompt: "dark theme",
    variantOptions: { variantCount: 2, creativeRange: "EXPLORE" },
    modelId: "GEMINI_3_PRO",
  }),
  client.callTool("generate_variants", { /* ... */ }),
  client.callTool("generate_variants", { /* ... */ }),
]);

Actual behavior

Stitch Transport Error: TypeError: fetch failed
  [cause]: SocketError: other side closed

One or more requests fail with no retry attempt. The transport's onerror sets isConnected = false, but the in-flight request is already lost.

Expected behavior

callTool should detect transient network errors (e.g. fetch failed, ECONNRESET, socket), reconnect, and retry the request once before throwing.

Environment

  • @google/stitch-sdk 0.0.3
  • Node.js 22
  • macOS
  • Reproducible with GEMINI_3_PRO model under concurrent variant generation

Activity

  1. added a commit that references this issue on Mar 18, 2026
    680eb34
  2. icebear0828 commented on Mar 18, 2026

    @icebear0828
    ContributorAuthor

    Additional finding: list_screens does not return screens generated via generate_screen_from_text

    During stress testing (10 and 20 concurrent generate_screen_from_text calls), we found:

    Test results

    Concurrency Client received Client failed (socket closed) list_screens API Web UI
    10 9/10 1 0 screens 2 screens visible
    20 17/20 3 0 screens 2 screens visible

    Project links

    Key observations

    1. Server-side generation continues after client disconnect — The client received TypeError: fetch failed (socket closed), but the Stitch backend completed generation. Screens are visible in the web UI.

    2. list_screens returns 0 for these projects — Even for screens that were successfully returned to the client, list_screens shows nothing. This means the tool description's advice ("try to get the screen with get_screen method later") may not work as a recovery path.

    3. Quota is consumed on disconnect — Failed requests still result in generated screens on the server. This reinforces that write operations should NOT be retried (which our PR implements).

    Impact on PR #115

    This confirms the retry design is correct:

    • Read operations (list_projects, get_screen): safe to retry, no side effects
    • Write operations (generate, edit, variants, create): must NOT retry — server may have already completed the operation
  3. drmikecrypto commented on Jun 27, 2026

    @drmikecrypto

    Opened #363 — callTool reconnects and retries once on transient network errors (fetch failed, socket closed, ECONNRESET).

    Write operations are not retried; only the transport reconnect path runs before a single retry, matching the concern in the thread about server-side completion after disconnect.

    Tests added in packages/sdk/test/unit/client.test.ts.

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

Metadata

Metadata

Assignees

No one assigned

    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