anthropics / anthropics/claude-code-action

Image MIME mismatch crashes runs: GIFs from issue bodies saved with .png extension, sent to Anthropic API as image/png

Open
#1,351 1 comment 0 reactions 0 assignees View on GitHub
bug p2
Dominant language
TypeScript
Stars
8.9k
Forks
2.1k
Avg merge
3d 9h
Merged PRs (30d)
10

Description

## Summary

When the action processes an issue (or PR) whose body references an image hosted on `github.com/user-attachments/assets/`, it downloads the bytes and saves them locally with a `.png` extension regardless of the actual content type. If Claude later opens that file with the `Read` tool, the tool result is sent to the Anthropic API with `media_type: image/png` but the bytes are a GIF (or other non-PNG format), and the API correctly rejects the request with HTTP 400.

This is reproducible end-to-end on a run that otherwise authenticates, triggers, and progresses normally — the run burns through ~$0.11 / 7 turns before failing irrecoverably.

## Reproduction

- Action ref: `anthropics/claude-code-action@beta` (commit `de8e0b9` at time of writing).
- Trigger: `issues: [opened, labeled]` workflow with `direct_prompt:` and `claude_code_oauth_token:` set; tag mode (default).
- Subject: an old issue whose body embeds a `user-attachments` image.

The image URL in our case is `https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f`. Downloading it and inspecting:

```
$ curl -sL 'https://github.com/user-attachments/assets/5ac382c7-e004-429b-8e35-7feb3e8f9c6f' | head -c 8 | xxd
00000000: 4749 4638 3961 8000 GIF89a..
```

It's a GIF. But the action saved it as `/tmp/github-images/image-1779583737858-0.png`.

## Error (from our run log)

```
{
"type": "assistant",
"message": {
"content": [{
"type": "tool_use",
"name": "Read",
"input": { "file_path": "/tmp/github-images/image-1779583737858-0.png" }
}]
}
}
```

Tool result:

```
{
"type": "user",
"message": {
"content": [{
"tool_use_id": "toolu_...",
"type": "tool_result",
"content": [{
"type": "image",
"source": {
"type": "base64",
"data": "R0lGODlhgACAAPQA…", // ← base64-decodes to "GIF89a…"
"media_type": "image/png" // ← but media_type claims PNG
}
}]
}]
}
}
```

Anthropic API response (HTTP 400):

```
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages.4.content.0.tool_result.content.0.image.source.base64: The image was specified using the image/png media type, but the image appears to be a image/gif image"
},
"request_id": "req_011CbLUWBhSwNTHFzj8a5uF4"
}
```

Run terminated with `is_error: true`, `total_cost_usd: 0.11139810`, `num_turns: 7`. Process exit code 1.

## Suspected root cause

The action's image-downloading code (around `src/github/data/fetcher.ts` per a code-search hit for "github-images" + the `image--.png` naming pattern) hard-codes a `.png` extension when persisting the downloaded blob. Downstream, the `Read` tool's MIME inference appears to be extension-based (defaulting to `image/png` for `.png` files), so the tool_result misrepresents the content type and the Anthropic API rightly rejects it.

Two fixable layers:

1. **Action download step** — sniff the magic bytes (or the response `Content-Type`) when saving and choose `.gif` / `.jpg` / `.webp` / etc. accordingly. GitHub's `user-attachments` endpoint returns the correct `Content-Type` header for the original asset.
2. **`Read` tool result builder** — content-sniff the bytes before setting `media_type` rather than trusting the file extension.

Either fix individually should resolve it; both is belt-and-braces.

## Impact

Any issue/PR triage workflow that runs Claude over a body containing a `user-attachments` GIF (animated screencaptures, the spinner the action itself uses in some templates, embedded animations, etc.) will fail mid-run after burning tokens. The failure isn't recoverable within the run — the malformed message stays in the conversation history, so subsequent retries hit the same 400.

## Workarounds we tried

- Removing the offending image from the issue body fixes the immediate run (per a follow-up retry by the issue reporter).
- No action-side knob exists to disable image fetching from issue bodies, so users with GIF-rich histories can't avoid the failure mode without editing each issue.

## Environment

- Action: `anthropics/claude-code-action@beta`
- Model: `claude-sonnet-4-20250514`
- API: direct Anthropic via OAuth token (`apiKeySource: ANTHROPIC_API_KEY`)
- Event: `issues.labeled`
- Workflow files: https://github.com/dxos/dxos/blob/main/.github/workflows/claude-composer-triage.yml
- Failing run: https://github.com/dxos/dxos/actions/runs/26347825246

Happy to provide the full job log if useful.

Contributor guide

Open the contributing guide

Research direction

Start with src/github/data/fetcher.ts and trace how user-attachments responses are saved, then inspect the Read tool result builder that assigns image media types. Reproduce with the GIF URL from the issue and verify that the saved format and Anthropic media_type match the downloaded bytes, including non-PNG images.

Written by the indexing model from the issue text.

Assessment

Tech stack
github, typescript
Domain
api, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.