midudev/autoskills · error · Error
git tree truncated for
Error message
git tree truncated for ${repo}@${sha.slice(0, 7)} What it means
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.
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
Example fix
// before
const tree = await fetchRepoTree("org/monorepo", sha); // throws: truncated
// after
const tree = await fetchRepoTree("org/skills", sha); // dedicated small repo Defensive patterns
Strategy: fallback
Validate before calling
// detect a likely-truncated tree before syncing huge repos
const res = await fetch(`https://api.github.com/repos/${repo}`, { headers: { Authorization: `Bearer ${GITHUB_TOKEN}` } });
const info = await res.json();
if (info.size > 500000) console.warn(`${repo} is large (${info.size}KB); tree API may truncate`); Try / catch
let tree;
try {
tree = await fetchRepoTree(repo, sha);
} catch (err) {
if (err.message.startsWith("git tree truncated")) {
// fallback: sparse clone instead of the Trees API
tree = await materializeSkillsFromSparseClone(repo, branch, skillNames, destRoot);
} else throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- GitHub 403 rate limit exceeded
- GitHub for
- GitHub rate limit exceeded
- git ls-remote failed for
- could not resolve HEAD for
AI-assisted analysis of midudev/autoskills@0ec725320d (2026-09-15).
Data as JSON: /api/errors/0c548c0419694002.
Report an issue: GitHub.
Appendix: source
Thrown at packages/autoskills/scripts/sync-skills.mjs:232
if (GITHUB_TOKEN) headers.Authorization = `Bearer ${GITHUB_TOKEN}`;
const ac = new AbortController();
const timer = setTimeout(() => ac.abort(), TARBALL_TIMEOUT_MS);
try {
const res = await fetch(url, { headers, signal: ac.signal });
if (!res.ok || !res.body) {
throw new Error(`Tarball fetch failed: ${res.status} ${url}`);
}
await pipeline(res.body, createWriteStream(destFile));
} finally {
clearTimeout(timer);
}
}
async function fetchRepoTree(repo, sha) {
const res = await ghFetch(`https://api.github.com/repos/${repo}/git/trees/${sha}?recursive=1`);
const body = await res.json();
if (body.truncated) {
throw new Error(`git tree truncated for ${repo}@${sha.slice(0, 7)}`);
}
return body.tree || [];
}
function findSkillDirsInTree(tree, skillName) {
// Returns array of { dir } where dir/SKILL.md exists in the tree.
const skillPaths = tree
.filter((t) => t.type === "blob" && /(^|\/)SKILL\.md$/i.test(t.path))
.map((t) => t.path);
const candidates = [];
for (const p of skillPaths) {
const parts = p.split("/");
parts.pop();
const parent = parts[parts.length - 1];
if (parent === skillName) {
candidates.push(parts.join("/"));
continue;View on GitHub (pinned to 0ec725320d)