anthropics / anthropics/claude-code-action

Write execution file in finally block so rate limit errors are parseable

Open Beginner friendly
#1,040 2 comments 2 reactions 0 assignees View on GitHub
bug dev-experience p2
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.