Skip to content

Anthropic provider tool web_fetch error result (url_not_allowed) causes AI SDK JSON.parse crash: SyntaxError: "[object Object]" is not valid JSON in convertToAnthropicMessagesPrompt (AI SDK v6 / @ai-sdk/anthropic) #11856

@HeisenbugSigurd

Description

@HeisenbugSigurd

Description

Anthropic provider tool web_fetch error result (url_not_allowed) causes AI SDK JSON.parse crash: SyntaxError: "[object Object]" is not valid JSON in convertToAnthropicMessagesPrompt (AI SDK v6 / @ai-sdk/anthropic)

Context

I’m building an AI chatbot with agent functionality using Vercel AI SDK v6. Under the hood I have several custom tools, and I also use Anthropic provider tools (server tools), specifically:

  • web_search (works fine)
  • web_fetch (intermittent issue)

The AI SDK centralizes provider translation, meaning the SDK is responsible for translating tool calls/results into the format Anthropic expects for subsequent requests.

What’s going wrong

When using Anthropic’s provider tool web_fetch with a specific URL under fotball.no, the provider returns a typed tool error result:

  • error_code: url_not_allowed

Even if that provider error is correct (or not), a provider-side tool error should not crash the SDK. However, the AI SDK then throws:

SyntaxError: "[object Object]" is not valid JSON

This error originates in the Anthropic adapter’s convertToAnthropicMessagesPrompt during serialization/preparation of messages.

Key observation about timing

The initial response stream appears to complete successfully and generates text. The crash manifests when the SDK attempts to serialize/prepare the conversation history including the failed tool result for a subsequent request, leading to a JSON parse failure.

This suggests the provider tool error object is not being handled/serialized consistently, and at some point an object is being coerced into the string "[object Object]", which is then passed to JSON.parse().


Expected Behavior

If Anthropic’s web_fetch returns an error tool result (e.g., web_fetch_tool_result_error), the AI SDK should:

  • preserve it as structured tool error data in message history/stream, and
  • continue the agent flow without throwing a JSON parsing exception when preparing subsequent requests.

Actual Behavior

When web_fetch returns web_fetch_tool_result_error with url_not_allowed, the AI SDK throws:

SyntaxError: "[object Object]" is not valid JSON
at JSON.parse (<anonymous>)
at convertToAnthropicMessagesPrompt (<project_root>/node_modules/@ai-sdk/anthropic/src/convert-to-anthropic-messages-prompt.ts:805:45)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async AnthropicMessagesLanguageModel.getArgs (<project_root>/node_modules/@ai-sdk/anthropic/src/anthropic-messages-language-model.ts:266:7)
at async AnthropicMessagesLanguageModel.doStream (<project_root>/node_modules/@ai-sdk/anthropic/src/anthropic-messages-language-model.ts:1051:9)
at async wrapStream (<project_root>/node_modules/@ai-sdk/devtools/dist/index.js:263:56)
at async fn (<project_root>/node_modules/ai/src/generate-text/stream-text.ts:1422:25)
at async <anonymous> (<project_root>/node_modules/ai/src/telemetry/record-span.ts:21:24)
at async _retryWithExponentialBackoff (<project_root>/node_modules/ai/src/util/retry-with-exponential-backoff.ts:96:12)
at async streamStep (<project_root>/node_modules/ai/src/generate-text/stream-text.ts:1374:15)

Evidence: Streams / Tool Results

AI SDK DevTools stream (translated from provider stream)

The relevant section (tool call + tool result):

{
  "type": "tool-call",
  "toolCallId": "srvtoolu_01JteKo9VRHDKZ1rdMXywnwD",
  "toolName": "webFetch",
  "input": "{\"url\": \"https://www.fotball.no/fotballdata/turnering/hjem/?fiksId=193156\"}",
  "providerExecuted": true
},
{
  "type": "tool-result",
  "toolCallId": "srvtoolu_01JteKo9VRHDKZ1rdMXywnwD",
  "toolName": "webFetch",
  "isError": true,
  "result": {
    "type": "web_fetch_tool_result_error",
    "errorCode": "url_not_allowed"
  }
}

Devtools "output" then shows:
"[object Object]" is not valid JSON

Anthropic provider stream (raw tool blocks):

{
  "type": "content_block_start",
  "index": 4,
  "content_block": {
    "type": "server_tool_use",
    "id": "srvtoolu_01JteKo9VRHDKZ1rdMXywnwD",
    "name": "web_fetch",
    "input": {}
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": ""
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": "{\"url\": \"https://www.fot"
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": "ball.no/fotballdata/"
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": "turn"
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": "ering/hjem/?fiksI"
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": "d=193"
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": "156"
  }
},
{
  "type": "content_block_delta",
  "index": 4,
  "delta": {
    "type": "input_json_delta",
    "partial_json": "\"}"
  }
},
{
  "type": "content_block_stop",
  "index": 4
},
{
  "type": "content_block_start",
  "index": 5,
  "content_block": {
    "type": "web_fetch_tool_result",
    "tool_use_id": "srvtoolu_01JteKo9VRHDKZ1rdMXywnwD",
    "content": {
      "type": "web_fetch_tool_result_error",
      "error_code": "url_not_allowed"
    }
  }
},
{
  "type": "content_block_stop",
  "index": 5
}

This indicates the provider is returning a structured, typed error result, and the SDK should be able to preserve and re-serialize it.

Post-workaround: tool result stored as 'error-json'

After implementing a workaround and continuing the conversation, AI SDK DevTools message history shows the failed tool result as:

{
  "type": "error-json",
  "value": "{\"type\":\"web_fetch_tool_result_error\",\"errorCode\":\"url_not_allowed\"}"
}

How to Reproduce

  1. Set up a chatbot using streamText with an Anthropic Claude model.

  2. Enable Anthropic’s provider-executed tools, including web_fetch (e.g., webFetch_20250305()).

  3. Configure tool allowlisting to permit the target domain (e.g., fotball.no).

  4. Trigger a provider tool call to: https://www.fotball.no/fotballdata/turnering/hjem/?fiksId=193156 or any fotball.nourl.

  5. Observe:
    The provider returns web_fetch_tool_result_error with error_code: url_not_allowed (even though domain is allowlisted).

The AI SDK then throws SyntaxError: "[object Object]" is not valid JSON originating in convertToAnthropicMessagesPrompt (around line 805).

Note: The provider returning url_not_allowed may itself be an Anthropic bug, but regardless the SDK should not crash when a provider tool returns a typed error result.


Current Workaround

I currently catch this specific error signature at the Express response streaming boundary and end the response gracefully so the conversation can continue (since the stream otherwise appears complete):

// Stream the response body to Express response
if (response.body) {
  const reader = response.body.getReader();
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      res.write(value);
    }
    res.end();
  } catch (error) {
    // Check if this is the SDK tool error serialization bug
    const errorMessage = error instanceof Error ? error.message : String(error);
    const isAnthropicToolError =
      errorMessage.includes('[object Object]') &&
      errorMessage.includes('is not valid JSON') &&
      errorMessage.includes('convertToAnthropicMessagesPrompt');

    if (isAnthropicToolError) {
      // This is the issue with webFetch
      // The SDK is failing to serialize error responses. The stream itself completed successfully,
      // so we can safely end the response and let the conversation continue.
      logger.warn('Anthropic tool error serialization bug detected - continuing gracefully', {
        conversationId: conversation.id,
        error: errorMessage,
      });
      res.end();
    } else {
      // Unknown error - handle as before
      logger.error('Error streaming response', { error, conversationId: conversation.id });
      if (!res.headersSent) {
        res.status(500).json({ error: 'Stream error' });
      } else {
        res.end();
      }
    }
  }
} else {
  res.end();
}

This avoids breaking the conversation, but does not fix the underlying SDK serialization/parsing issue.


Additional Context

This issue appears only when:

  • using Anthropic’s provider-executed tools (web_fetch_20250305)
  • a provider tool execution returns an error response (so far I have only tested with url_not_allowed, since that is the only error I have encountered

The initial response stream still completes writing the text output; the failure seems to me to be in preparing subsequent requests that include the error tool result in history.


Suggested Fix / Direction

In @ai-sdk/anthropic, convertToAnthropicMessagesPrompt should robustly handle provider tool error results.

Possible approaches:

  • detect provider tool error result objects (e.g., { type: 'web_fetch_tool_result_error', errorCode: ... }) and serialize them consistently using JSON.stringify rather than allowing implicit object-to-string coercion

  • normalize these tool error results into an Anthropic-compatible “tool_result” representation that can round-trip in message history

  • ensure any parsing logic does not attempt JSON.parse on a non-JSON string like "[object Object]"

Even if the underlying provider error is unexpected, the adapter should treat it as structured tool error data and never crash.

AI SDK Version

{
  "ai": "^6.0.31",
  "@ai-sdk/anthropic": "^3.0.12",
  "@ai-sdk/react": "^3.0.25"
}

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

Labels

ai/providerrelated to a provider package. Must be assigned together with at least one `provider/*` labelbugSomething isn't working as documentedprovider/anthropicIssues related to the @ai-sdk/anthropic providersupport

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions