Claude Files API — upload, reference, or retention failure
The Files API lets you upload documents once and reference them by <code>file_id</code> across many Messages requests — until size limits, format constraints, or expired references bite.
Quick fix (TL;DR)
file_id in a document or image block on subsequent Messages calls, and (c) refreshing expired file_ids by re-uploading rather than assuming permanence.Real error messages you'll see
These are the exact strings returned by the Claude API service and its SDKs when this error occurs. Copy-paste-searching any of them should land on this page.
anthropic.BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'File size 42.1 MB exceeds the maximum allowed size of 32 MB for application/pdf.'}}anthropic.BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'Unsupported file type: application/vnd.openxmlformats-officedocument.wordprocessingml.document. Supported types: application/pdf, text/plain, image/*.'}}anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File file_abc123 not found.'}}
Reference
Files API upload constraints (verify current values in docs)
| File type | Max size | Notes |
|---|---|---|
application/pdf | 32 MB | ~100 pages typical |
text/plain | 32 MB | Any UTF-8 text |
image/png, image/jpeg, image/gif, image/webp | 5 MB | Also 8000×8000 pixel max |
| Others (docx, xlsx, csv) | Not supported | Convert to PDF or plain text first |
File reference block shapes
| Content type | Block shape | Where allowed |
|---|---|---|
| PDF or text | {"type": "document", "source": {"type": "file", "file_id": "..."}} | User messages |
| Image | {"type": "image", "source": {"type": "file", "file_id": "..."}} | User messages |
| Inline PDF | {"type": "document", "source": {"type": "base64", ...}} | For one-off use |
Root causes, ranked by frequency
Based on developer reports across Claude API forums, GitHub issues, and Anthropic community during 2025–2026.
- 26%File exceeds size limit. Common with scanned PDFs (image-heavy) or downstream-generated reports without compression.
- 20%Unsupported MIME type. docx, xlsx, and csv are not directly supported. Convert first.
- 14%file_id used across workspaces. Files are workspace-scoped; a file uploaded on workspace A cannot be referenced from workspace B.
- 10%file_id expired. Default retention is ~30 days; older references return 404.
- 8%Wrong beta header. Files API is behind the
files-api-2025-04-14beta header (verify current name). Requests without it fail. - 7%Client sending file inline when it should be referenced. Some SDKs implicitly base64-encode; combined with size limits this causes 413 instead of 400.
- 7%PDF encrypted / password-protected. Fails at server-side extraction.
- 8%Image resolution too high. 8000×8000 pixel cap; larger images rejected even under the size cap.
Fixes — copy-paste solutions
Upload with pre-flight validation and error routing
Validate the file client-side before spending bandwidth on the upload. Route oversize files through a chunker (page-split PDFs), unsupported types through a converter (docx → PDF).
import os import mimetypes import anthropic from pathlib import Path client = anthropic.Anthropic( default_headers={"anthropic-beta": "files-api-2025-04-14"} # verify current beta ) LIMITS = { "application/pdf": 32 * 1024 * 1024, "text/plain": 32 * 1024 * 1024, "image/png": 5 * 1024 * 1024, "image/jpeg": 5 * 1024 * 1024, "image/gif": 5 * 1024 * 1024, "image/webp": 5 * 1024 * 1024, } def upload_file(path: str, purpose: str = "user_data") -> str: """Validate then upload. Returns file_id.""" p = Path(path) if not p.is_file(): raise FileNotFoundError(path) mime, _ = mimetypes.guess_type(str(p)) if mime not in LIMITS: raise ValueError( f"Unsupported MIME {mime!r} for {path}. Convert to PDF or text first." ) size = p.stat().st_size if size > LIMITS[mime]: raise ValueError( f"{path} is {size/1024/1024:.1f} MB — over {LIMITS[mime]/1024/1024:.0f} MB " f"limit for {mime}. Split or compress." ) with p.open("rb") as f: uploaded = client.beta.files.upload(file=(p.name, f, mime)) return uploaded.id file_id = upload_file("report.pdf") # Reference the uploaded file in a Messages call response = client.messages.create( model="claude-opus-4-7", max_tokens=1000, messages=[{ "role": "user", "content": [ {"type": "document", "source": {"type": "file", "file_id": file_id}}, {"type": "text", "text": "Summarise the key financial risks."}, ], }], ) print(response.content[0].text)
Split oversize PDFs by page count
Use pypdf to split a large PDF into per-N-page chunks, each under the size limit. Upload each chunk, keep the file_ids in order, and reference them as separate document blocks in one Messages call.
from pypdf import PdfReader, PdfWriter from pathlib import Path def split_pdf(source: str, out_dir: str = "chunks", pages_per_chunk: int = 30) -> list[str]: reader = PdfReader(source) Path(out_dir).mkdir(exist_ok=True) chunks = [] for start in range(0, len(reader.pages), pages_per_chunk): writer = PdfWriter() for i in range(start, min(start + pages_per_chunk, len(reader.pages))): writer.add_page(reader.pages[i]) chunk_path = f"{out_dir}/chunk_{start//pages_per_chunk:03d}.pdf" with open(chunk_path, "wb") as f: writer.write(f) chunks.append(chunk_path) return chunks # Upload each chunk, keep order chunks = split_pdf("giant_report.pdf", pages_per_chunk=30) file_ids = [upload_file(c) for c in chunks] # Reference all chunks in one Messages call — Claude reads them as a sequence content = [ {"type": "document", "source": {"type": "file", "file_id": fid}} for fid in file_ids ] + [{"type": "text", "text": "Summarise findings across all sections."}] response = client.messages.create( model="claude-opus-4-7", max_tokens=2000, messages=[{"role": "user", "content": content}], )
Manage file_id lifecycle explicitly
Files are retained ~30 days by default. Build a lifecycle-aware store: record file_id + upload_date + original hash in your DB, refresh files before they expire, and clean up deliberately when done.
import anthropic import hashlib from datetime import datetime, timedelta from dataclasses import dataclass from typing import Optional client = anthropic.Anthropic( default_headers={"anthropic-beta": "files-api-2025-04-14"} ) # Your file record table @dataclass class FileRecord: logical_id: str # your app's ID file_id: str # Anthropic's ID source_path: str sha256: str uploaded_at: datetime ttl_days: int = 30 @property def expires_at(self) -> datetime: return self.uploaded_at + timedelta(days=self.ttl_days) @property def expiring_soon(self) -> bool: return self.expires_at - datetime.utcnow() < timedelta(days=3) def compute_sha256(path: str) -> str: h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(65536), b""): h.update(chunk) return h.hexdigest() def refresh_if_expiring(record: FileRecord) -> FileRecord: if not record.expiring_soon: return record # Old file will disappear soon — re-upload new_file_id = upload_file(record.source_path) # from previous fix # Delete the old one to keep the workspace clean try: client.beta.files.delete(record.file_id) except anthropic.NotFoundError: pass # already gone return FileRecord( logical_id=record.logical_id, file_id=new_file_id, source_path=record.source_path, sha256=record.sha256, uploaded_at=datetime.utcnow(), ) # Nightly job — refresh anything expiring in the next 3 days for record in all_file_records(): updated = refresh_if_expiring(record) if updated.file_id != record.file_id: save_record(updated)
Prevention checklist
Ship these seven safeguards once and this error stops appearing in your logs.
- Validate MIME type and file size client-side before every upload — do not surprise the user with a server-side 400.
- Split PDFs over 32MB into per-page-range chunks; upload each and reference in sequence.
- Store file_id + logical_id + upload timestamp + content hash in your DB — treat file_id as ephemeral.
- Add a nightly job that refreshes files within 3 days of expiry — never let a stale file_id break production.
- Delete files when their source data changes — orphaned files pile up and hit workspace limits.
- Convert docx / xlsx to PDF before upload (LibreOffice, Docx2PDF) — the Files API does not accept Office formats directly.
- Rate-limit uploads at the client — bursts of large files can exhaust the upload endpoint quotas.
Frequently asked questions
Related errors & hubs
Get the weekly AI-error digest
New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.