anthropics / anthropics/claude-code-action
Write execution file in finally block so rate limit errors are parseable
- Dominant language
- TypeScript
- Stars
- 8.9k
- Forks
- 2.1k
- Avg merge
- 3d 9h
- Merged PRs (30d)
- 10
Description
I ran into this problem on a closed-source project that uses this action. Investigation with Claude Code turned up the following recommendation.
## Problem
When Claude Code hits a rate limit, the SDK streams a `rate_limit_event` message followed by a `result` message with `is_error: true`, then throws. Because the execution file write in `base-action/src/run-claude-sdk.ts` is after the try/catch block (not in a `finally`), the file is never written and the `execution_file` output is empty.
This means callers cannot distinguish rate limits from other failures — both surface as exit code 1 with a minified SDK stack trace.
## Where
`base-action/src/run-claude-sdk.ts` lines ~160–188:
```typescript
try {
for await (const message of query({ prompt, options: sdkOptions })) {
messages.push(message);
// ... messages are collected, including rate_limit_event and result
}
} catch (error) {
console.error("SDK execution error:", error);
throw new Error(`SDK execution error: ${error}`);
// ← execution file write below is unreachable
}
// This block never executes on rate limit errors
const result: ClaudeRunResult = { conclusion: "failure" };
try {
await writeFile(EXECUTION_FILE, JSON.stringify(messages, null, 2));
result.executionFile = EXECUTION_FILE;
} catch (error) {
core.warning(`Failed to write execution file: ${error}`);
}
```
## Suggested fix
Move the execution file write into a `finally` block. The `messages` array is populated before the throw, so the data is available:
```typescript
} catch (error) {
console.error("SDK execution error:", error);
throw new Error(`SDK execution error: ${error}`);
} finally {
if (messages.length > 0) {
try {
await writeFile(EXECUTION_FILE, JSON.stringify(messages, null, 2));
core.setOutput("execution_file", EXECUTION_FILE);
} catch (e) {
core.warning(`Failed to write execution file: ${e}`);
}
}
}
```
Additionally, a dedicated `error_type` output (e.g. `rate_limit`) would let workflows handle rate limits without grepping JSON — but the execution file alone would unblock the use case.
## Impact
Workflows using `continue-on-error: true` to post-process failures cannot detect rate limits vs real errors, making it impossible to:
- Show a warning annotation instead of a hard failure
- Surface the reset time to PR authors
- Skip re-runs that will hit the same limit
## Reproduction
Trigger a code review while rate-limited. The `execution_file` output is empty and the only signal is the generic `SDK execution error: Error: Claude Code process exited with code 1` in the step log.
Contributor guide
Research direction
Read base-action/src/run-claude-sdk.ts around lines 160–188, then trace the existing execution_file output handling. Exercise the rate-limit/error path described in the issue and confirm that the collected messages are written and execution_file is populated before the failure is surfaced.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- ci-cd, tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100