{"record":{"id":"0c548c0419694002","repo":"midudev/autoskills","slug":"git-tree-truncated-for-repo-sha-slice-0-7","errorCode":null,"errorMessage":"git tree truncated for ${repo}@${sha.slice(0, 7)}","messagePattern":"git tree truncated for (.+?)@(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/autoskills/scripts/sync-skills.mjs","lineNumber":232,"sourceCode":"  if (GITHUB_TOKEN) headers.Authorization = `Bearer ${GITHUB_TOKEN}`;\n  const ac = new AbortController();\n  const timer = setTimeout(() => ac.abort(), TARBALL_TIMEOUT_MS);\n  try {\n    const res = await fetch(url, { headers, signal: ac.signal });\n    if (!res.ok || !res.body) {\n      throw new Error(`Tarball fetch failed: ${res.status} ${url}`);\n    }\n    await pipeline(res.body, createWriteStream(destFile));\n  } finally {\n    clearTimeout(timer);\n  }\n}\n\nasync function fetchRepoTree(repo, sha) {\n  const res = await ghFetch(`https://api.github.com/repos/${repo}/git/trees/${sha}?recursive=1`);\n  const body = await res.json();\n  if (body.truncated) {\n    throw new Error(`git tree truncated for ${repo}@${sha.slice(0, 7)}`);\n  }\n  return body.tree || [];\n}\n\nfunction findSkillDirsInTree(tree, skillName) {\n  // Returns array of { dir } where dir/SKILL.md exists in the tree.\n  const skillPaths = tree\n    .filter((t) => t.type === \"blob\" && /(^|\\/)SKILL\\.md$/i.test(t.path))\n    .map((t) => t.path);\n\n  const candidates = [];\n  for (const p of skillPaths) {\n    const parts = p.split(\"/\");\n    parts.pop();\n    const parent = parts[parts.length - 1];\n    if (parent === skillName) {\n      candidates.push(parts.join(\"/\"));\n      continue;","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/midudev/autoskills/blob/0ec725320d2137253ab2e68e7ba8a072148e741a/packages/autoskills/scripts/sync-skills.mjs#L214-L250","documentation":"fetchRepoTree calls the GitHub Git Trees API with ?recursive=1 to list every file in a commit. GitHub truncates trees exceeding size/entry limits and sets body.truncated=true; the script throws rather than syncing from an incomplete file list, which would silently miss skills.","triggerScenarios":"GET /repos/<repo>/git/trees/<sha>?recursive=1 returns a JSON body with truncated:true — typically repos with tens of thousands of files or very large trees.","commonSituations":"Syncing skills from a huge monorepo; a repo with vendored dependencies inflating the tree; the truncated flag appearing only on large repos so it surfaces after the script works on smaller ones.","solutions":["Sync from a repo with a smaller tree (split skills into a dedicated repo)","Use a narrower ref/SHA whose tree is under the limit","Add a fallback path using `git clone --depth 1 --filter=blob:none` or sparse checkout instead of the Trees API","Check whether GitHub's tree limits have changed and whether an API upgrade offers pagination"],"exampleFix":"// before\nconst tree = await fetchRepoTree(\"org/monorepo\", sha); // throws: truncated\n// after\nconst tree = await fetchRepoTree(\"org/skills\", sha); // dedicated small repo","handlingStrategy":"fallback","validationCode":"// detect a likely-truncated tree before syncing huge repos\nconst res = await fetch(`https://api.github.com/repos/${repo}`, { headers: { Authorization: `Bearer ${GITHUB_TOKEN}` } });\nconst info = await res.json();\nif (info.size > 500000) console.warn(`${repo} is large (${info.size}KB); tree API may truncate`);","typeGuard":null,"tryCatchPattern":"let tree;\ntry {\n  tree = await fetchRepoTree(repo, sha);\n} catch (err) {\n  if (err.message.startsWith(\"git tree truncated\")) {\n    // fallback: sparse clone instead of the Trees API\n    tree = await materializeSkillsFromSparseClone(repo, branch, skillNames, destRoot);\n  } else throw err;\n}","preventionTips":["Keep skill sources in small dedicated repos, not monorepos","Use sparse clone as an alternative listing mechanism for big repos","Prefer pinned SHAs whose trees are known to fit","Watch for GitHub changing tree-size limits"],"tags":["github","api","git","limit"],"backgroundTag":"http-error-response","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"}