vercel-labs/skills · error · ArchiveValidationError
Archive contains too many files (${state.entries}). Maximum
Error message
Archive contains too many files (${state.entries}). Maximum is ${limits.extractMaxFiles}. Set SKILLS_EXTRACT_MAX_FILES to override. What it means
While streaming archive entries, incrementEntry counts each file and throws ArchiveValidationError once state.entries exceeds limits.extractMaxFiles. This is a zip-bomb / resource-exhaustion guard: extraction stops before the archive can create an unbounded number of files. The cap is configurable via the SKILLS_EXTRACT_MAX_FILES environment variable.
Source
Thrown at src/download-source.ts:67
function isPathSafe(basePath: string, targetPath: string): boolean {
const normalizedBase = normalize(resolve(basePath));
const normalizedTarget = normalize(resolve(targetPath));
return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
}
function validateArchivePath(path: string): string | null {
const normalized = path.replace(/\\/g, '/').replace(/^\.\//, '');
if (!normalized || normalized.endsWith('/')) return normalized;
if (normalized.startsWith('/') || /^[a-zA-Z]:\//.test(normalized)) return null;
if (normalized.split('/').includes('..')) return null;
return normalized;
}
function incrementEntry(state: ExtractState, size: number, limits: DownloadLimits): void {
state.entries += 1;
if (state.entries > limits.extractMaxFiles) {
throw new ArchiveValidationError(
`Archive contains too many files (${state.entries}). Maximum is ${limits.extractMaxFiles}. Set SKILLS_EXTRACT_MAX_FILES to override.`
);
}
state.bytes += size;
if (state.bytes > limits.extractMaxBytes) {
throw new ArchiveValidationError(
`Archive extracts to more than ${limits.extractMaxBytes} bytes. Set SKILLS_EXTRACT_MAX_BYTES to override.`
);
}
}
async function downloadToFile(
url: string,
targetFile: string,
limits: DownloadLimits
): Promise<void> {
const response = await fetch(url, {View on GitHub (pinned to 435076e789)
Solutions
- Raise the cap: SKILLS_EXTRACT_MAX_FILES=5000 skills add <source>
- Inspect the archive entry count first (unzip -l file.zip | tail -1) and prune it if it contains unrelated files
- Prefer installing from a git source or local path, which bypasses archive extraction limits
- Only raise the limit for trusted sources — it exists to stop zip bombs
Example fix
# before skills add ./huge-bundle.zip # too many files # after SKILLS_EXTRACT_MAX_FILES=10000 skills add ./huge-bundle.zip
Defensive patterns
Strategy: validation
Validate before calling
// Count entries before extracting
import { execFileSync } from 'node:child_process';
function zipEntryCount(path: string): number {
const out = execFileSync('unzip', ['-l', path], { encoding: 'utf8' });
const m = out.match(/(\d+) files?/);
return m ? Number(m[1]) : -1;
} Type guard
null
Try / catch
try {
await extractArchive(file, limits);
} catch (err) {
if (err instanceof ArchiveValidationError && err.message.includes('too many files')) {
process.env.SKILLS_EXTRACT_MAX_FILES = String(limit * 10); // retry with raised cap if trusted
} else throw err;
} Prevention
- Only raise SKILLS_EXTRACT_MAX_FILES for archives you built yourself
- Prefer git or local-path sources for large bundles
When it happens
Trigger: Extracting an archive containing more files than the configured limit (default cap set by DownloadLimits), e.g. a monorepo snapshot or a skill bundle with thousands of entries. Also triggered maliciously by a zip-bomb designed to exhaust inodes/disk.
Common situations: Adding a large multi-skill repository as a zip instead of via git, vendored archives with node_modules included, or environments where someone lowered SKILLS_EXTRACT_MAX_FILES and forgot.
Related errors
- Archive extracts to more than ${limits.extractMaxBytes} byte
- Archive exceeds maximum unpacked size
- Download is larger than ${limits.downloadMaxBytes} bytes. Se
- Archive contains too many files
- Invalid zip archive: ${label} is out of bounds
AI-assisted analysis of vercel-labs/skills@435076e789 (2026-08-28).
Data as JSON: /api/errors/ddc4dabbe6ab16a5.
Report an issue: GitHub.