Skip to content

Global fetch monkey-patch for OpenRouter diagnostics is not concurrency-safe #411

Description

Summary

installOpenRouterDebugFetch reassigns globalThis.fetch for the duration of a run and restores it blindly in a finally. If two runOpenWikiAgent calls overlap in the same process, their install/restore steps corrupt each other — the wrapper can leak permanently or be lost. It also only captures error bodies for OpenRouter, leaving Anthropic/OpenAI/Bedrock 4xx/5xx bodies invisible to classifyError.

Details

// src/agent/index.ts:1399-1452
function installOpenRouterDebugFetch(
  options: OpenWikiRunOptions,
): OpenRouterFetchCapture {
  const originalFetch = globalThis.fetch;
  // ...
  globalThis.fetch = (async (input, init) => { /* ... */ }) satisfies typeof fetch;

  return {
    // ...
    restore: () => {
      globalThis.fetch = originalFetch;
    },
  };
}

Failure mode with overlapping runs (subagents, tests running in parallel, a long-lived host process):

  1. Run A installs, capturing the real fetch as originalFetch.
  2. Run B installs, capturing A's patched fetch as its "original."
  3. Run A finishes and restores the real fetch.
  4. Run B finishes and restores A's patched fetch — now leaked into the global forever.

Depending on interleaving, the patch can also be lost mid-run.

Suggested fix

Attach diagnostics per-model via the provider client's configuration.fetch hook — the same mechanism already used for Codex and Vertex (src/agent/index.ts:710,853) — instead of a process-global swap. This removes the concurrency hazard and generalizes error-body capture to every provider, not just OpenRouter.

If a global swap must be retained short-term, guard it with a reentrancy counter and a stack-based restore so nested installs compose correctly.

Location

src/agent/index.ts:1399-1452

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