{"record":{"id":"3ebd0607fbb7205d","repo":"midudev/autoskills","slug":"empty-tarball-in-destdir","errorCode":null,"errorMessage":"Empty tarball in ${destDir}","messagePattern":"Empty tarball in (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/autoskills/scripts/sync-skills.mjs","lineNumber":323,"sourceCode":"      if (shouldSkipSkillFile(rel)) continue;\n      const dest = join(destRoot, skillName, rel);\n      await downloadRawFile(repo, sha, blob.path, dest);\n    }\n    found.set(skillName, join(destRoot, skillName));\n  }\n  return found;\n}\n\nfunction extractTarball(tarFile, destDir) {\n  const r = spawnSync(\"tar\", [\"-xzf\", tarFile, \"-C\", destDir], {\n    stdio: FLAGS.verbose ? \"inherit\" : \"pipe\",\n  });\n  if (r.status !== 0) {\n    throw new Error(`tar extract failed (status ${r.status})`);\n  }\n  const entries = readdirSync(destDir);\n  const root = entries.find((e) => statSync(join(destDir, e)).isDirectory());\n  if (!root) throw new Error(`Empty tarball in ${destDir}`);\n  return join(destDir, root);\n}\n\nfunction runGit(args, label) {\n  const result = spawnSync(\"git\", args, {\n    encoding: \"utf-8\",\n    stdio: [\"ignore\", \"pipe\", \"pipe\"],\n  });\n  if (result.status !== 0) {\n    throw new Error(`${label} failed: ${result.stderr.trim() || \"unknown error\"}`);\n  }\n  return result.stdout;\n}\n\nfunction materializeSkillsFromSparseClone(repo, branch, skillNames, destRoot) {\n  const repoDir = join(destRoot, \"repo\");\n  runGit(\n    [","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/midudev/autoskills/blob/0ec725320d2137253ab2e68e7ba8a072148e741a/packages/autoskills/scripts/sync-skills.mjs#L305-L341","documentation":"After extracting, extractTarball reads destDir expecting GitHub tarball layout: exactly one top-level directory (repo-owner-sha/) containing the content. If no directory entry is found, it throws this error. It guards against empty or unexpectedly-shaped archives before the rest of the sync proceeds.","triggerScenarios":"readdirSync(destDir) contains no entry that statSync reports as a directory — the tarball extracted nothing, only files at the root, or the extraction target is empty.","commonSituations":"A zero-byte or corrupt tarball that tar 'successfully' extracted nothing from; a hand-crafted or non-GitHub archive without the single root folder convention; leftover empty destDir from a failed prior run.","solutions":["Delete destDir/cached tarball and re-download from GitHub so the archive has the standard root directory","Verify the tarball with `tar -tzf <file> | head` — it should show one top-level folder","Ensure the tarball comes from GitHub's codeload endpoint, not a custom archive format","Check disk space; a full disk can yield a partial extract with no directories"],"exampleFix":"// before\n// hand-built tarball with files at root\n// after\n// use the GitHub codeload tarball URL\nconst url = `https://codeload.github.com/${repo}/tar.gz/refs/heads/${branch}`;","handlingStrategy":"validation","validationCode":"// inspect archive shape before extraction\nconst listing = execSync(`tar -tzf ${tarFile}`, { encoding: \"utf8\" });\nconst roots = new Set(listing.split(\"\\n\").filter(Boolean).map(l => l.split(\"/\")[0]));\nif (roots.size !== 1) throw new Error(`unexpected archive layout: ${[...roots]}`);","typeGuard":null,"tryCatchPattern":"try {\n  await syncSkills(opts);\n} catch (err) {\n  if (err.message.startsWith(\"Empty tarball\")) {\n    console.error(\"Archive had no content directory; re-download from GitHub codeload\");\n    // delete destDir + cached tarball, retry once\n  } else throw err;\n}","preventionTips":["Only use GitHub codeload tarballs, which always have a single root dir","Verify archives with `tar -tzf | head` showing one top-level folder","Remove stale empty destDirs before retrying extraction","Guard the pipeline so failed downloads never 'succeed' with 0-byte files"],"tags":["tar","filesystem","archive"],"backgroundTag":"empty-result-set","analyzedSha":"0ec725320d2137253ab2e68e7ba8a072148e741a","analyzedAt":"2026-09-15T14:12:31.090Z","contentChangedAt":"2026-09-15T14:12:31.090Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}