vercel-labs/skills · error

Archive missing root SKILL.md

Error message

Archive missing root SKILL.md

What it means

After extracting a registry artifact, the provider requires a root-level SKILL.md (files must contain the exact key 'SKILL.md'). If every extracted entry lives in a subdirectory (or the manifest is missing), 'Archive missing root SKILL.md' is thrown.

Source

Thrown at src/providers/wellknown.ts:750

      if (!Number.isFinite(size) || size < 0) throw new Error('Invalid tar entry size');
      offset += 512;

      // Reject symlinks and hard links. Skip directories and metadata entries.
      if (typeFlag === 0x32 || typeFlag === 0x31) {
        throw new Error('Archive links are not supported');
      }

      const isFile = typeFlag === 0 || typeFlag === 0x30;
      if (isFile) {
        const content = tar.subarray(offset, offset + size);
        this.addArchiveFile(files, path, new Uint8Array(content), runningTotal);
      }

      offset += Math.ceil(size / 512) * 512;
    }

    if (!files.has('SKILL.md')) throw new Error('Archive missing root SKILL.md');
    return files;
  }

  private readTarString(buffer: Uint8Array, offset: number, length: number): string {
    const slice = buffer.subarray(offset, offset + length);
    const nul = slice.indexOf(0);
    return new TextDecoder().decode(nul >= 0 ? slice.subarray(0, nul) : slice);
  }

  /**
   * Convert a user-facing URL to a skill URL.
   * For well-known, this extracts the base domain and constructs the proper path.
   * Uses agent-skills as the primary path for new URLs.
   */
  toRawUrl(url: string): string {
    try {
      const parsed = new URL(url);
      if (url.toLowerCase().endsWith('/skill.md')) {

View on GitHub (pinned to 435076e789)

Solutions

  1. Repackage from inside the skill directory so SKILL.md is at archive root: cd my-skill && tar -czf ../skill.tar.gz .
  2. Verify: tar -tzf skill.tar.gz should list './SKILL.md' or 'SKILL.md' first
  3. Fix filename casing to exactly 'SKILL.md'
  4. Re-publish the corrected artifact

Example fix

# before (archive root contains my-skill/SKILL.md)
cd .. && tar -czf skill.tar.gz my-skill
# after
cd my-skill && tar -czf ../skill.tar.gz .   # SKILL.md at root
Defensive patterns

Strategy: validation

Validate before calling

import { execSync } from 'node:child_process';
const root = execSync(`tar -tzf ${file}`).toString().split('\n')[0].replace(/^\./, '');
if (root !== '' && !root.endsWith('SKILL.md')) {
  throw new Error('Archive wraps skill in a directory; SKILL.md must be at root');
}

Type guard

function isMissingSkillMd(e: unknown): e is Error {
  return e instanceof Error && /missing root SKILL\.md/i.test(e.message);
}

Try / catch

try { await provider.fetchArtifact(url); }
catch (e) {
  if (isMissingSkillMd(e)) return await provider.fetchArtifact(url.replace('/skill.tar.gz', '/skill-at-root.tar.gz'));
  throw e;
}

Prevention

When it happens

Trigger: An archive where SKILL.md sits inside a folder (e.g. my-skill/SKILL.md) or is named differently (skill.md, Skill.md), or was excluded during packaging — extraction succeeds but the 'SKILL.md' key check fails.

Common situations: Packaging with a wrapper directory ('cd parent && tar -czf skill.tar.gz myskill/'); case-mismatched filenames from macOS/Windows; forgetting to include the manifest at all.

Related errors


AI-assisted analysis of vercel-labs/skills@435076e789 (2026-08-28). Data as JSON: /api/errors/38994ee1aeb03745. Report an issue: GitHub.