Skip to content

Commit 6cc5b34

Browse files
ltawfikclaude
andcommitted
Fix: Remove cost_usd field and handle GeneratorExit
The CLI only sends total_cost_usd, not cost_usd. This PR: - Removes the non-existent cost_usd field from ResultMessage - Makes total_cost_usd optional since it may be missing - Handles GeneratorExit gracefully in subprocess transport - Removes unnecessary cancel scope call Fixes the error users were experiencing: - KeyError: 'cost_usd' - Field required [type=missing, input_value=...] The anyio cancel scope error still appears in logs but doesn't affect functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 54bff2e commit 6cc5b34

File tree

3 files changed

+8
-11
lines changed

3 files changed

+8
-11
lines changed

src/claude_code_sdk/_internal/client.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,9 @@ def _parse_message(self, data: dict[str, Any]) -> Message | None:
4747

4848
match data["type"]:
4949
case "user":
50-
# Extract just the content from the nested structure
5150
return UserMessage(content=data["message"]["content"])
5251

5352
case "assistant":
54-
# Parse content blocks
5553
content_blocks: list[ContentBlock] = []
5654
for block in data["message"]["content"]:
5755
match block["type"]:
@@ -79,20 +77,18 @@ def _parse_message(self, data: dict[str, Any]) -> Message | None:
7977
case "system":
8078
return SystemMessage(
8179
subtype=data["subtype"],
82-
data=data, # Pass through all data
80+
data=data,
8381
)
8482

8583
case "result":
86-
# Map total_cost to total_cost_usd for consistency
8784
return ResultMessage(
8885
subtype=data["subtype"],
89-
cost_usd=data["cost_usd"],
9086
duration_ms=data["duration_ms"],
9187
duration_api_ms=data["duration_api_ms"],
9288
is_error=data["is_error"],
9389
num_turns=data["num_turns"],
9490
session_id=data["session_id"],
95-
total_cost_usd=data["total_cost"],
91+
total_cost_usd=data.get("total_cost_usd"),
9692
usage=data.get("usage"),
9793
result=data.get("result"),
9894
)

src/claude_code_sdk/_internal/transport/subprocess_cli.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,18 @@ async def read_stderr() -> None:
190190

191191
try:
192192
data = json.loads(line_str)
193-
yield data
193+
try:
194+
yield data
195+
except GeneratorExit:
196+
# Handle generator cleanup gracefully
197+
return
194198
except json.JSONDecodeError as e:
195199
if line_str.startswith("{") or line_str.startswith("["):
196200
raise SDKJSONDecodeError(line_str, e) from e
197201
continue
198202

199203
except anyio.ClosedResourceError:
200204
pass
201-
finally:
202-
tg.cancel_scope.cancel()
203205

204206
await self._process.wait()
205207
if self._process.returncode is not None and self._process.returncode != 0:

src/claude_code_sdk/types.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,12 @@ class ResultMessage:
7575
"""Result message with cost and usage information."""
7676

7777
subtype: str
78-
cost_usd: float
7978
duration_ms: int
8079
duration_api_ms: int
8180
is_error: bool
8281
num_turns: int
8382
session_id: str
84-
total_cost_usd: float
83+
total_cost_usd: float | None = None
8584
usage: dict[str, Any] | None = None
8685
result: str | None = None
8786

0 commit comments

Comments
 (0)