Claude Files API upload failed — size, format, and retention errors (2026) — Fix Guide (2026) | AI Error Hub
Home Providers Claude Files API upload
Claude Files API · Upload Severity: Medium HTTP 400

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.

By Ahmed R. · Senior AI Infrastructure Engineer Published 2026-07-26 Verified 2026-07-26

Quick fix (TL;DR)

Resolution: The Files API accepts PDFs, plain text, and images up to a per-file limit (typically 32MB PDF, 5MB image, current values in the docs). Files persist ~30 days by default, longer with beta headers. Fix upload errors by (a) validating file size and MIME type before upload, (b) using the 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.

400 — file too large
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.'}}
400 — unsupported MIME type
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/*.'}}
404 — file_id expired or wrong workspace
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 typeMax sizeNotes
application/pdf32 MB~100 pages typical
text/plain32 MBAny UTF-8 text
image/png, image/jpeg, image/gif, image/webp5 MBAlso 8000×8000 pixel max
Others (docx, xlsx, csv)Not supportedConvert to PDF or plain text first

File reference block shapes

Content typeBlock shapeWhere 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-14 beta 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

Fix #1

Upload with pre-flight validation and error routing

Never hit the API without checking size, MIME type, and page count first.

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).

upload_with_validation.py
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)
For consumer-uploaded documents, always validate before upload — a 40MB user file causes a 400 that the user perceives as a bug, not a limitation.
Fix #2

Split oversize PDFs by page count

When a single PDF exceeds 32MB, chunk it into multiple uploads.

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.

split_and_upload.py
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}],
)
Claude handles multi-document input coherently but token count adds up quickly. A 30-page PDF is ~15K tokens; 5 such chunks is 75K tokens on the input side. Use prompt caching to keep costs manageable if you query the same document set many times.
Fix #3

Manage file_id lifecycle explicitly

List, delete, and refresh — do not assume permanence.

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.

file_lifecycle.py
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)
Compute a content hash (sha256) at upload time. On refresh, verify the source is unchanged before re-uploading — silent source drift is a common data-quality bug.

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

The default retention is around 30 days; extended retention is available via beta headers. Anthropic reserves the right to delete files earlier for policy violations. Always check current retention terms in the Files API docs.
Yes — that is the value of Files API. Upload once, reference by file_id in as many Messages calls as you need. Each reference is billed at the standard input token cost for the document's content.
Yes — Claude extracts text and images from PDFs and counts both. A visually-heavy PDF (charts, images) can consume 2-3× the tokens of the plain text equivalent. Convert to plain text for cost-sensitive use cases if the visual content is not needed.
File uploads use a separate quota (uploads per minute) — not your Messages ITPM or RPM. However, each Messages reference to a file counts input tokens for the file content against your standard ITPM.
Workspace-scoped. A file_id from workspace A cannot be used from workspace B — you get 404. Design your architecture to keep uploads in the workspace where they will be consumed.

Get the weekly AI-error digest

New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.