Checks
Strands Version
1.1.0
Node.js Version
22.17.0
Operating System
macOS 15.1
Installation Method
npm
Steps to Reproduce
- define a tool with multiple input fields
- configure BedrockModel with sufficiently low
maxTokens
- prompt agent to call the tool with sufficiently long input to exceed max tokens
Expected Behavior
Failures due to output token limits being exceeded should generate a MaxTokensError rather than failing to parse truncated JSON. The python SDK exhibits this behavior.
Actual Behavior
The contentBlockStop arrives with truncated input; parse fails before messageStop { stopReason: 'maxTokens' } is processed. This deviates from the behavior observed in the python SDK.
Additional Context
Minimal failing typescript example:
import { Agent, BedrockModel, MaxTokensError, ModelError, tool } from '@strands-agents/sdk'
import { z } from 'zod'
const writeSections = tool({
name: 'write_sections',
description: 'Submit the six sections of a short essay, one per field.',
inputSchema: z.object({
intro: z.string().describe('Opening section.'),
body: z.string().describe('Body section.'),
middle: z.string().describe('Middle section.'),
conclusion: z.string().describe('Conclusion section.'),
summary: z.string().describe('Summary section.'),
epilogue: z.string().describe('Epilogue section.'),
}),
callback: () => 'sections received',
})
const PROMPT = [
'Call write_sections once with all six fields populated. Use one Lorem Ipsum',
'sentence per field. Do not include any preamble or commentary — go straight',
'to the tool call. The sentences to use:',
'',
'intro: Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'body: Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
'middle: Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
'conclusion: Nisi ut aliquip ex ea commodo consequat duis aute irure dolor.',
'summary: In reprehenderit in voluptate velit esse cillum dolore eu fugiat.',
'epilogue: Excepteur sint occaecat cupidatat non proident sunt in culpa qui.',
].join('\n')
const MAX_TOKENS = 180
const model = new BedrockModel({
modelId: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0',
maxTokens: MAX_TOKENS,
})
const agent = new Agent({
model,
tools: [writeSections],
})
// Fails with ModelError(SyntaxError)
await agent.invoke(PROMPT)
Passing python example
import os
import traceback
from strands import Agent, tool
from strands.models import BedrockModel
from strands.types.exceptions import MaxTokensReachedException
@tool
def write_sections(
intro: str,
body: str,
middle: str,
conclusion: str,
summary: str,
epilogue: str,
) -> str:
"""Submit the six sections of a short essay, one per field."""
return "sections received"
PROMPT = "\n".join(
[
"Call write_sections once with all six fields populated. Use one Lorem Ipsum",
"sentence per field. Do not include any preamble or commentary — go straight",
"to the tool call. The sentences to use:",
"",
"intro: Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"body: Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"middle: Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.",
"conclusion: Nisi ut aliquip ex ea commodo consequat duis aute irure dolor.",
"summary: In reprehenderit in voluptate velit esse cillum dolore eu fugiat.",
"epilogue: Excepteur sint occaecat cupidatat non proident sunt in culpa qui.",
]
)
MAX_TOKENS = 180
model = BedrockModel(
model_id="global.anthropic.claude-sonnet-4-5-20250929-v1:0",
max_tokens=MAX_TOKENS,
)
agent = Agent(model=model, tools=[write_sections])
# Fails correctly with MaxTokensReachedException
agent(PROMPT)
Possible Solution
Regression introduce in #680
Restore the deferred-throw pattern but use stopReason to disambiguate root cause from symptom:
} catch (e: unknown) {
if (e instanceof SyntaxError) {
deferredParseError = e
}
}
// ... after the loop ...
if (finalStopReason === 'maxTokens') {
throw new MaxTokensError(..., stoppedMessage) // root cause wins
}
if (deferredParseError !== undefined) {
throw deferredParseError // model emitted bad JSON
}
This should keep the scenario in #680 working (when stopReason is not maxTokens, the SyntaxError still surfaces) and fixes the truncation case. The Python SDK uses the same disambiguation strategy.
Related Issues
No response
Checks
Strands Version
1.1.0
Node.js Version
22.17.0
Operating System
macOS 15.1
Installation Method
npm
Steps to Reproduce
maxTokensExpected Behavior
Failures due to output token limits being exceeded should generate a
MaxTokensErrorrather than failing to parse truncated JSON. The python SDK exhibits this behavior.Actual Behavior
The
contentBlockStoparrives with truncated input; parse fails beforemessageStop { stopReason: 'maxTokens' }is processed. This deviates from the behavior observed in the python SDK.Additional Context
Minimal failing typescript example:
Passing python example
Possible Solution
Regression introduce in #680
Restore the deferred-throw pattern but use stopReason to disambiguate root cause from symptom:
This should keep the scenario in #680 working (when stopReason is not maxTokens, the SyntaxError still surfaces) and fixes the truncation case. The Python SDK uses the same disambiguation strategy.
Related Issues
No response