vercel-labs/skills · error

Downloaded URL is not a valid SKILL.md file or supported arc

Error message

Downloaded URL is not a valid SKILL.md file or supported archive

What it means

Final fallback error in downloadSource(): the downloaded non-empty file is neither a valid SKILL.md (isValidSkillMarkdown) nor an archive that tryExtractArchive can extract. It means the URL's content type is simply unsupported by the CLI.

Source

Thrown at src/download-source.ts:286

    const downloadedStats = await stat(downloadedFile);
    if (downloadedStats.size === 0) {
      throw new Error('Downloaded URL is empty');
    }

    if (await isValidSkillMarkdown(downloadedFile)) {
      const skillDir = join(tempDir, 'skill');
      await mkdir(skillDir, { recursive: true });
      await writeFile(join(skillDir, 'SKILL.md'), await readFile(downloadedFile));
      return { rootDir: skillDir, tempDir, kind: 'skill-md' };
    }

    await mkdir(extractDir, { recursive: true });
    if (await tryExtractArchive(downloadedFile, extractDir, limits)) {
      const rootDir = (await getSingleTopLevelDirectory(extractDir)) ?? extractDir;
      return { rootDir, tempDir, kind: 'archive' };
    }

    throw new Error('Downloaded URL is not a valid SKILL.md file or supported archive');
  } catch (error) {
    await rm(tempDir, { recursive: true, force: true }).catch(() => {});
    throw error;
  }
}

View on GitHub (pinned to 435076e789)

Solutions

  1. Open the URL with curl and confirm it serves raw content, not HTML (use raw.githubusercontent.com for GitHub files)
  2. If adding a single file, ensure it is a valid SKILL.md with the required frontmatter (name, description)
  3. If adding an archive, use .zip or .tar.gz, which are the supported formats
  4. Alternatively clone the repo and run 'skills add <local-path>'

Example fix

# before
skills add https://github.com/org/repo/blob/main/SKILL.md
# after
skills add https://raw.githubusercontent.com/org/repo/main/SKILL.md
Defensive patterns

Strategy: try-catch

Validate before calling

const head = await fetch(url);
const ct = head.headers.get('content-type') ?? '';
const ok = /text\/plain|markdown|octet-stream|zip|gzip|tar/.test(ct);
if (!ok) throw new Error(`URL serves unsupported type: ${ct}`);

Type guard

function isUnsupportedDownload(e: unknown): e is Error {
  return e instanceof Error && /not a valid SKILL\.md file or supported archive/.test(e.message);
}

Try / catch

try { await downloadSource(url); }
catch (e) {
  if (isUnsupportedDownload(e)) {
    // fall back to cloning the repo and adding from a local path
    const dir = await cloneRepo(gitUrlFor(url));
    return addFromLocalPath(dir);
  }
  throw e;
}

Prevention

When it happens

Trigger: downloadSource() downloads a file, isValidSkillMarkdown() rejects it (missing frontmatter/name fields), and tryExtractArchive() returns false because the bytes are not gzip/zip/tar — then this generic error is thrown.

Common situations: Adding a URL that serves HTML (GitHub blob page instead of raw), a README.md without valid SKILL.md frontmatter, a .tar.bz2/.7z archive, or a JSON/YAML manifest the CLI does not understand.

Related errors


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