Describe the bug
The SDK correctly generates idempotency keys for network-level retries within a session. The gap is at the agent level: when an agent framework retries a tool call as a new invocation (after a timeout, crash, or model loop), a new session starts with a new auto-generated key. The result is a second charge.
This is distinct from the SDK's retry handling — it's an orchestration-layer problem. The fix belongs above the tool, not inside it: generate a stable request_id from the tool call arguments before execution, claim it in durable storage, and return the cached receipt on any retry with the same ID.
I built a small Python guard that does exactly this: SafeAgent. Happy to show how it wraps the Stripe toolkit tools if useful, or contribute a reference implementation.
To Reproduce
- Build a LangChain or CrewAI agent using the Stripe toolkit
- Call create_payment_intent or create_charge as a tool
- Simulate a timeout before the agent receives confirmation
- Agent retries the tool call as a new invocation
- Two charges are created
Expected behavior
A retry of the same logical tool call with the same arguments should return the original receipt without executing a second charge.
Code snippets
OS
Linux
Language version
Python 3.10+
Library version
Latest
API version
Current
Additional context
This is an orchestration-layer problem, not an SDK bug. The fix belongs above the tool: generate a stable request_id from tool call arguments before execution, claim it in durable storage, return cached receipt on retry. Built a reference implementation: https://github.com/azender1/SafeAgent
Describe the bug
The SDK correctly generates idempotency keys for network-level retries within a session. The gap is at the agent level: when an agent framework retries a tool call as a new invocation (after a timeout, crash, or model loop), a new session starts with a new auto-generated key. The result is a second charge.
This is distinct from the SDK's retry handling — it's an orchestration-layer problem. The fix belongs above the tool, not inside it: generate a stable request_id from the tool call arguments before execution, claim it in durable storage, and return the cached receipt on any retry with the same ID.
I built a small Python guard that does exactly this: SafeAgent. Happy to show how it wraps the Stripe toolkit tools if useful, or contribute a reference implementation.
To Reproduce
Expected behavior
A retry of the same logical tool call with the same arguments should return the original receipt without executing a second charge.
Code snippets
OS
Linux
Language version
Python 3.10+
Library version
Latest
API version
Current
Additional context
This is an orchestration-layer problem, not an SDK bug. The fix belongs above the tool: generate a stable request_id from tool call arguments before execution, claim it in durable storage, return cached receipt on retry. Built a reference implementation: https://github.com/azender1/SafeAgent