bytedance/deer-flow · warning · HTTPException

Skill archive member is too large to preview

Error message

Skill archive member is too large to preview

What it means

HTTP 413 from _read_skill_archive_member: the ZIP entry's declared uncompressed file_size in its ZipInfo header exceeds MAX_SKILL_ARCHIVE_MEMBER_BYTES (16 MiB, artifacts.py:38). The header check happens before any decompression so a zip-bomb-sized member is refused without spending CPU/bytes on it.

Solutions

  1. Remove the oversized member from the skill pack and rebuild the .skill archive
  2. Split large assets out of the skill; reference them by URL or install step instead of bundling
  3. If you are the consumer, preview other members — only the oversized entry is refused
Defensive patterns

Strategy: validation

Validate before calling

import zipfile

MAX_SKILL_ARCHIVE_MEMBER_BYTES = 16 * 1024 * 1024

def member_within_cap(zip_path: str, internal_path: str) -> bool:
    with zipfile.ZipFile(zip_path) as z:
        return z.getinfo(internal_path).file_size <= MAX_SKILL_ARCHIVE_MEMBER_BYTES

Try / catch

if resp.status_code == 413 and "skill archive member" in resp.text.lower():
    skip_preview(internal_path)  # show metadata only

Prevention

When it happens

Trigger: GET artifact preview for a .skill archive whose member (e.g. bundled model, dataset, or vendored dependency) declares >16 MiB uncompressed size in the ZIP central directory.

Common situations: Skill packs vendoring large binaries or node_modules-like trees, users zipping whole working directories into .skill files, malicious/naive archives with inflated headers.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/019dfe09e2fcb4e2. Report an issue: GitHub.

Appendix: source

Thrown at backend/app/gateway/routers/artifacts.py:221

    )
    return ranged_content, 206, headers


def is_text_file_by_content(path: Path, sample_size: int = 8192) -> bool:
    """Check if file is text by examining content for null bytes."""
    try:
        with open(path, "rb") as f:
            chunk = f.read(sample_size)
            # Text files shouldn't contain null bytes
            return b"\x00" not in chunk
    except Exception:
        return False


def _read_skill_archive_member(zip_ref: zipfile.ZipFile, info: zipfile.ZipInfo) -> bytes:
    """Read a .skill archive member while enforcing an uncompressed size cap."""
    if info.file_size > MAX_SKILL_ARCHIVE_MEMBER_BYTES:
        raise HTTPException(status_code=413, detail="Skill archive member is too large to preview")

    chunks: list[bytes] = []
    total_read = 0
    with zip_ref.open(info, "r") as src:
        while chunk := src.read(_SKILL_ARCHIVE_READ_CHUNK_SIZE):
            total_read += len(chunk)
            if total_read > MAX_SKILL_ARCHIVE_MEMBER_BYTES:
                raise HTTPException(status_code=413, detail="Skill archive member is too large to preview")
            chunks.append(chunk)
    return b"".join(chunks)


def _extract_file_from_skill_archive(zip_path: Path, internal_path: str) -> bytes | None:
    """Extract a file from a .skill ZIP archive.

    Args:
        zip_path: Path to the .skill file (ZIP archive).
        internal_path: Path to the file inside the archive (e.g., "SKILL.md").

View on GitHub (pinned to 1dd6ba1acb)