midudev/autoskills · error · Error
raw fetch failed
Error message
raw fetch failed: ${res.status} ${url} What it means
downloadRawFile fetches an individual file from raw.githubusercontent.com and streams it to disk. A non-ok response or missing body throws this error with the HTTP status and URL — most often 404 for a path that doesn't exist at that ref, or 403 rate limiting.
Solutions
- Verify the exact URL opens in a browser — fix the path/ref if 404
- Set GITHUB_TOKEN and ensure it is sent for raw requests to lift rate limits
- Re-run the sync to recover from transient GitHub 5xx/timeout
- Check file name case matches the repo exactly (Linux raw serving is case-sensitive)
Example fix
// before
await downloadRawFile("https://raw.githubusercontent.com/org/repo/main/Skill.md", dest);
// after
await downloadRawFile("https://raw.githubusercontent.com/org/repo/main/SKILL.md", dest); Defensive patterns
Strategy: retry
Validate before calling
// confirm the raw URL exists before streaming to disk
const probe = await fetch(url, { method: "HEAD" });
if (probe.status === 404) throw new Error(`raw file missing: ${url}`); Try / catch
try {
await downloadRawFile(url, dest);
} catch (err) {
const m = err.message.match(/^raw fetch failed: (\d{3})/);
if (m) {
if (m[1] === "404") throw new Error(`Permanent: file gone — ${url}`);
if (m[1] === "403" || m[1].startsWith("5")) await retryWithBackoff(() => downloadRawFile(url, dest));
} else throw err;
} Prevention
- Send GITHUB_TOKEN on raw requests to avoid 403 rate limits
- Match file paths and case exactly as they exist in the repo
- Treat 404 as permanent and re-sync the tree before retrying
- Add retries with backoff for 403/5xx statuses
When it happens
Trigger: fetch(rawUrl) yields res.ok === false or res.body === null while downloading a single raw file — wrong path/branch in the tree, deleted file, or rate-limited unauthenticated raw access.
Common situations: A SKILL.md listed in the git tree was deleted between tree fetch and file download (race); case-sensitivity mismatch in the file path; unauthenticated raw request rate limit exceeded.
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 for
- Tarball fetch failed
- GitHub rate limit exceeded
- download failed for
- ⚠️ No se pudo crear la release en GitHub (¿tienes gh…
AI-assisted analysis of midudev/autoskills@0ec725320d (2026-09-15).
Data as JSON: /api/errors/bee3d2e92961e6e3.
Report an issue: GitHub.
Appendix: source
Thrown at packages/autoskills/scripts/sync-skills.mjs:279
}
candidates.sort((a, b) => a.length - b.length);
return candidates;
}
async function downloadRawFile(repo, sha, repoPath, destFile) {
const url = `https://raw.githubusercontent.com/${repo}/${sha}/${repoPath}`;
const headers = { "User-Agent": "autoskills-sync" };
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(`raw fetch failed: ${res.status} ${url}`);
}
mkdirSync(dirname(destFile), { recursive: true });
await pipeline(res.body, createWriteStream(destFile));
} finally {
clearTimeout(timer);
}
}
async function materializeSkillsFromTree(repo, sha, skillNames, destRoot) {
const tree = await fetchRepoTree(repo, sha);
const found = new Map(); // skillName → relative dir within destRoot
for (const skillName of skillNames) {
const dirs = findSkillDirsInTree(tree, skillName);
if (dirs.length === 0) continue;
const pick = dirs[0];
const blobs = tree.filter(
(t) =>
t.type === "blob" &&View on GitHub (pinned to 0ec725320d)