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

  1. Verify the exact URL opens in a browser — fix the path/ref if 404
  2. Set GITHUB_TOKEN and ensure it is sent for raw requests to lift rate limits
  3. Re-run the sync to recover from transient GitHub 5xx/timeout
  4. 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

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


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)