# Attach images and files to ClaudeAgent and CodexAgent runs

> Agno's ClaudeAgent and CodexAgent accept Image and File inputs from run(), arun() and the AgentOS runs endpoint. Audio and video are refused with HTTP 400.

- Published: 2026-10-12
- Author: Ashpreet Bedi
- Categories: Changelog
- Canonical: https://www.agno.com/blog/attach-images-and-files-to-claudeagent-and-codexagent-runs
- Markdown: https://www.agno.com/blog/attach-images-and-files-to-claudeagent-and-codexagent-runs.md

Agno's `ClaudeAgent` and `CodexAgent` save each attachment as a file under `<cwd>/.agno/uploads/<run_id>/` and list its path under the prompt, the way you would hand a file to someone at a terminal. Claude opens each path with its own Read tool, and Codex also receives images as native image inputs.

```python
from pathlib import Path

from agno.agents.claude import ClaudeAgent
from agno.db.sqlite import SqliteDb
from agno.media import File, Image

agent = ClaudeAgent(
    name="Analyst",
    cwd=".",
    allowed_tools=["Read"],
    db=SqliteDb(db_file="tmp/agents.db"),
)

agent.run(
    "Total the amount column.",
    images=[Image(content=Path("chart.png").read_bytes(), format="png")],
    files=[File(content=b"item,amount\nA,120\nB,80\n", filename="sales.csv", mime_type="text/csv")],
    session_id="attachments",
)
```

We ran the same call with a 40-byte PNG and a stand-in for the Claude Agent SDK. This is the prompt Claude received, and both files existed at those paths while the turn ran.

```text
# real output, with the workspace and run id shortened to <cwd> and <run_id>
Total the amount column.
Attached files. Read them before answering; images and PDFs can be opened with the file tools:
- <cwd>/.agno/uploads/<run_id>/image-1.png (image/png, 40 bytes)
- <cwd>/.agno/uploads/<run_id>/sales.csv (text/csv, 23 bytes)
```

When the run ends, Agno deletes the run's upload folder, and `keep_uploads=True` leaves it in place. The attachments stay on the run's stored input, so a follow-up turn on another replica gets them back. Agno writes them under that machine's workspace before the harness resumes and tells it when a path has moved.

Over HTTP, the AgentOS runs endpoint takes the same attachments in its `files` multipart field. Audio and video have no reader in either harness, so AgentOS turns them away before the SDK starts.

```text
# real output: POST /agents/claude/runs with memo.wav (audio/wav) in the files field
400 {"detail":"This agent does not support audio inputs; images and files are placed in the workspace for the harness to read."}
```

Earlier releases answered that request with a 200 and ran the turn on the message text alone.

Staging only ever writes inside the active workspace. A run id that is more than one path component, or an uploads folder that is a symlink, stops the run with `MediaStagingError`, and a staging that fails partway removes every file it created, even with `keep_uploads=True`. Files that were already in the folder stay where they are.

```text
# real output: agno.agents._media.stage_media with a traversal run id and a symlinked uploads folder
run_id='../../etc': MediaStagingError: Attachment run_id must be a single nonempty path component
symlinked uploads dir: MediaStagingError: Attachment directories must not be symlinks
```

See the [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/frameworks/claude-agent-sdk/claude_media.py), and learn more about [attachments for Claude and Codex](https://docs.agno.com/agent-os/multi-framework/media) in the documentation.
