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
-
Set up a chatbot using streamText with an Anthropic Claude model.
-
Enable Anthropic’s provider-executed tools, including web_fetch (e.g., webFetch_20250305()).
-
Configure tool allowlisting to permit the target domain (e.g., fotball.no).
-
Trigger a provider tool call to: https://www.fotball.no/fotballdata/turnering/hjem/?fiksId=193156 or any fotball.nourl.
-
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
Description
Anthropic provider tool
web_fetcherror result (url_not_allowed) causes AI SDK JSON.parse crash:SyntaxError: "[object Object]" is not valid JSONinconvertToAnthropicMessagesPrompt(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_fetchwith a specific URL underfotball.no, the provider returns a typed tool error result:error_code: url_not_allowedEven if that provider error is correct (or not), a provider-side tool error should not crash the SDK. However, the AI SDK then throws:
This error originates in the Anthropic adapter’s
convertToAnthropicMessagesPromptduring 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 toJSON.parse().Expected Behavior
If Anthropic’s
web_fetchreturns an error tool result (e.g.,web_fetch_tool_result_error), the AI SDK should:Actual Behavior
When
web_fetchreturnsweb_fetch_tool_result_errorwithurl_not_allowed, the AI SDK throws: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 JSONAnthropic 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
Set up a chatbot using streamText with an Anthropic Claude model.
Enable Anthropic’s provider-executed tools, including web_fetch (e.g., webFetch_20250305()).
Configure tool allowlisting to permit the target domain (e.g., fotball.no).
Trigger a provider tool call to:
https://www.fotball.no/fotballdata/turnering/hjem/?fiksId=193156or anyfotball.nourl.Observe:
The provider returns
web_fetch_tool_result_errorwitherror_code: url_not_allowed(even though domain is allowlisted).The AI SDK then throws
SyntaxError: "[object Object]" is not valid JSONoriginating in convertToAnthropicMessagesPrompt (around line 805).Note: The provider returning
url_not_allowedmay 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):
This avoids breaking the conversation, but does not fix the underlying SDK serialization/parsing issue.
Additional Context
This issue appears only when:
web_fetch_20250305)url_not_allowed, since that is the only error I have encounteredThe 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,convertToAnthropicMessagesPromptshould 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 coercionnormalize these tool error results into an Anthropic-compatible
“tool_result”representation that can round-trip in message historyensure 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