Skip to content

AgentToolkit.call() ignores response.ok — every 4xx/5xx is returned to the agent as a successful result #62

Description

@yakimoto

AgentToolkit.call() never checks response.ok:

private async call(method: string, path: string, body?: Record<string, unknown>): Promise<unknown> {
  const response = await fetch(`${this.baseUrl}${path}`, { ... });
  return response.json();          // <-- no status check
}

Every one of the 10 tools routes through this. A 402, 403, 429, or 500 is parsed as JSON and returned to the agent as if it were a successful result.

Failure scenario

An agent calls wave_create_stream with an API key whose rate_limit_tier is null (a real, currently-open condition — wave-av/wave-gateway#622). The gateway returns 402 with a billing error body. call() returns that body. The tool resolves successfully. The agent proceeds as though it has a live stream and continues issuing calls against a stream that was never created.

The same shape hides a 429: instead of backing off, the agent treats the rate-limit body as data and keeps hammering — the exact behaviour .wave-rules/require-rate-limit-awareness.md exists to prevent.

It also fails less gracefully than it looks: if the error body is not JSON (an HTML 502 from an edge, say), response.json() throws a raw SyntaxError with no status, no URL, and no body — actively worse for debugging than the error it is masking.

Fix

#58 added WaveToolError and assertOk() in src/tools/shared.ts for exactly this, and the three new toolkits use them: throw on non-2xx, carrying status and the response body verbatim, with .isRateLimited flagging 429. AgentToolkit should adopt the same path:

const response = await fetch(...);
await assertOk(toolName, response);
return response.json();

This is a breaking change and should be treated as one, which is why #58 deliberately left it alone rather than sneaking it in. Callers who today receive an error body as a resolved value will start seeing a thrown WaveToolError. That is the correct behaviour, but it warrants a minor-version bump and a CHANGELOG note.

Sequencing: do #60 first. Changing error semantics across all 10 tools with no running test suite is how a regression ships quietly.

Pre-existing; surfaced while building #58 for wave-av/wave-context#72.

Activity

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

    needs-triageOn the board but missing Type/Area/Priority

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions