{"record":{"id":"38994ee1aeb03745","repo":"vercel-labs/skills","slug":"archive-missing-root-skill-md","errorCode":null,"errorMessage":"Archive missing root SKILL.md","messagePattern":"Archive missing root SKILL\\.md","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/providers/wellknown.ts","lineNumber":750,"sourceCode":"\n      if (!Number.isFinite(size) || size < 0) throw new Error('Invalid tar entry size');\n      offset += 512;\n\n      // Reject symlinks and hard links. Skip directories and metadata entries.\n      if (typeFlag === 0x32 || typeFlag === 0x31) {\n        throw new Error('Archive links are not supported');\n      }\n\n      const isFile = typeFlag === 0 || typeFlag === 0x30;\n      if (isFile) {\n        const content = tar.subarray(offset, offset + size);\n        this.addArchiveFile(files, path, new Uint8Array(content), runningTotal);\n      }\n\n      offset += Math.ceil(size / 512) * 512;\n    }\n\n    if (!files.has('SKILL.md')) throw new Error('Archive missing root SKILL.md');\n    return files;\n  }\n\n  private readTarString(buffer: Uint8Array, offset: number, length: number): string {\n    const slice = buffer.subarray(offset, offset + length);\n    const nul = slice.indexOf(0);\n    return new TextDecoder().decode(nul >= 0 ? slice.subarray(0, nul) : slice);\n  }\n\n  /**\n   * Convert a user-facing URL to a skill URL.\n   * For well-known, this extracts the base domain and constructs the proper path.\n   * Uses agent-skills as the primary path for new URLs.\n   */\n  toRawUrl(url: string): string {\n    try {\n      const parsed = new URL(url);\n      if (url.toLowerCase().endsWith('/skill.md')) {","sourceCodeStart":732,"sourceCodeEnd":768,"githubUrl":"https://github.com/vercel-labs/skills/blob/435076e78988e1e6ec40d00b0b1d76bdbbc5419a/src/providers/wellknown.ts#L732-L768","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Repackage from inside the skill directory so SKILL.md is at archive root: cd my-skill && tar -czf ../skill.tar.gz .","Verify: tar -tzf skill.tar.gz should list './SKILL.md' or 'SKILL.md' first","Fix filename casing to exactly 'SKILL.md'","Re-publish the corrected artifact"],"exampleFix":"# before (archive root contains my-skill/SKILL.md)\ncd .. && tar -czf skill.tar.gz my-skill\n# after\ncd my-skill && tar -czf ../skill.tar.gz .   # SKILL.md at root","handlingStrategy":"validation","validationCode":"import { execSync } from 'node:child_process';\nconst root = execSync(`tar -tzf ${file}`).toString().split('\\n')[0].replace(/^\\./, '');\nif (root !== '' && !root.endsWith('SKILL.md')) {\n  throw new Error('Archive wraps skill in a directory; SKILL.md must be at root');\n}","typeGuard":"function isMissingSkillMd(e: unknown): e is Error {\n  return e instanceof Error && /missing root SKILL\\.md/i.test(e.message);\n}","tryCatchPattern":"try { await provider.fetchArtifact(url); }\ncatch (e) {\n  if (isMissingSkillMd(e)) return await provider.fetchArtifact(url.replace('/skill.tar.gz', '/skill-at-root.tar.gz'));\n  throw e;\n}","preventionTips":["Package from inside the skill directory, not its parent","Verify with tar -tzf that SKILL.md appears at the archive root","Use the exact filename 'SKILL.md' (case-sensitive)"],"tags":["archive","skill-md","packaging","wellknown-provider"],"backgroundTag":"missing-manifest-in-package","analyzedSha":"435076e78988e1e6ec40d00b0b1d76bdbbc5419a","analyzedAt":"2026-08-28T17:47:53.369Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}