Skip to content

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

@richburdon

Description

@richburdon

Summary

When the action processes an issue (or PR) whose body references an image hosted on github.com/user-attachments/assets/<uuid>, 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-<timestamp>-<i>.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

Happy to provide the full job log if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingp2Non-showstopper bug or popular feature request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions