JuliusBrussee/caveman · error

SKILL.md must be 1..262144 bytes

Error message

SKILL.md must be 1..262144 bytes

What it means

cavemannifyImportedSkill validates the raw SKILL.md bytes before processing. An empty file or one larger than 256*1024 bytes (262144) is rejected with this size-limit error, keeping imported skills within a sane size budget.

Solutions

  1. Trim the SKILL.md to under 262144 bytes — move bulky reference material to separate linked files
  2. Populate the empty SKILL.md with real frontmatter and instructions
  3. Minify/compress tables and examples inside the manifest
  4. Verify size with wc -c SKILL.md before importing

Example fix

// before
// SKILL.md is 400000 bytes of embedded logs
// after
// split: keep SKILL.md < 262144 bytes, move logs to reference/logs.md
wc -c skills/my-skill/SKILL.md
Defensive patterns

Strategy: validation

Validate before calling

import { statSync } from "node:fs";
const { size } = statSync(manifestPath);
if (size === 0 || size > 256 * 1024) throw new Error(`SKILL.md size ${size} outside 1..262144`);

Type guard

null

Try / catch

try { import(input); } catch (e) { if (e.message === "SKILL.md must be 1..262144 bytes") { console.error("Trim SKILL.md below 256 KiB or fill the empty file"); } else throw e; }

Prevention

When it happens

Trigger: Importing a SKILL.md that is 0 bytes or exceeds 262144 bytes, e.g. a skill containing huge generated tables, logs, or embedded datasets.

Common situations: Machine-generated skill files; concatenating docs into one manifest; empty placeholder files created by a scaffold; pasting large transcripts into SKILL.md.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/23797c6907d221fd. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:1585

	benchmarks: { status: "required"; fixtures: string[] };
	evidence_status: "unevaluated";
	publication: { status: "blocked"; blockers: string[] };
};

function importedSkillSource(input: string): { root: string; file: string } {
	let resolved: string;
	try { resolved = realpathSync(resolve(process.cwd(), expandTilde(input))); } catch {
		throw new Error(`source does not exist: ${input}`);
	}
	const stat = lstatSync(resolved);
	const file = stat.isDirectory() ? join(resolved, SKILL_MD) : resolved;
	if (!stat.isDirectory() && basename(resolved) !== SKILL_MD) throw new Error("source file must be named SKILL.md");
	if (!hasFile(file)) throw new Error(`SKILL.md missing under ${resolved}`);
	return { root: stat.isDirectory() ? resolved : dirname(resolved), file };
}

function cavemannifyImportedSkill(bytes: Buffer): { body: string; removedDuplicates: number; removedSeparators: number } {
	if (bytes.length === 0 || bytes.length > 256 * 1024) throw new Error("SKILL.md must be 1..262144 bytes");
	if (bytes.includes(0)) throw new Error("SKILL.md contains NUL bytes");
	const text = bytes.toString("utf8");
	if (!Buffer.from(text, "utf8").equals(bytes)) throw new Error("SKILL.md must be valid UTF-8");
	if (/-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:sk|ghp|github_pat|xox[baprs])[-_][A-Za-z0-9_-]{16,}/i.test(text)) {
		throw new Error("SKILL.md contains credential-shaped material");
	}
	const split = splitSkillMarkdown(bytes);
	if (!split) throw new Error("SKILL.md needs closed YAML frontmatter");
	const blocks = split.bodyText.trim().split(/\r?\n(?:[ \t]*\r?\n)+/);
	const seen = new Set<string>();
	const kept: string[] = [];
	let removedDuplicates = 0;
	let removedSeparators = 0;
	for (const block of blocks) {
		const clean = block.trim();
		if (/^(?:---+|\*\*\*+|___+)$/.test(clean)) {
			removedSeparators++;
			continue;

View on GitHub (pinned to 3ee70a1026)