heygen-com/hyperframes · error
HTTP ${res.status} fetching ${url}
Error message
HTTP ${res.status} fetching ${url} What it means
Thrown by fetchManifest when the HTTP response from a remote skills manifest URL has a non-OK status code. Unlike the malformed-manifest guard, this fires before JSON parsing — the server itself rejected the request. The URL and status code are included in the message.
Source
Thrown at packages/cli/src/utils/skillsManifest.ts:647
/**
* Narrow an untrusted JSON payload to a SkillsManifest, or throw a clear error.
* Guards against a CDN serving an error page (or a malformed manifest) as 200 —
* without this, a bad shape surfaces later as a cryptic crash in diffSkills.
*/
function asSkillsManifest(data: unknown, sourceLabel: string): SkillsManifest {
const m = data as Partial<SkillsManifest> | null;
if (!m || typeof m !== "object" || typeof m.skills !== "object" || m.skills === null) {
throw new Error(`Malformed skills manifest from ${sourceLabel}`);
}
return m as SkillsManifest;
}
async function fetchManifest(url: string): Promise<SkillsManifest> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
try {
const res = await fetch(url, { signal: controller.signal, headers: { Connection: "close" } });
if (!res.ok) throw new Error(`HTTP ${res.status} fetching ${url}`);
return asSkillsManifest(await res.json(), url);
} finally {
clearTimeout(timeout);
}
}
/**
* Resolve main's live HEAD sha via `git ls-remote`. GitHub's branch-raw CDN
* (raw.githubusercontent.com/<owner>/<repo>/main/...) can serve stale content
* for minutes after a push; a SHA-pinned raw URL is immediately consistent.
* Returns null when git/network is unavailable so callers fall back to main.
*/
async function remoteHeadSha(repoSlug: string): Promise<string | null> {
try {
const { stdout } = await execFileAsync(
"git",
["ls-remote", `https://github.com/${repoSlug}.git`, "refs/heads/main"],
{ timeout: FETCH_TIMEOUT_MS, env: { ...process.env, GIT_TERMINAL_PROMPT: "0" } },View on GitHub (pinned to c2996c8626)
Solutions
- Check the status code in the error — 404 means wrong path/repo, 403 means private or rate-limited, 5xx means server error.
- Verify the URL is correct by opening it in a browser.
- If rate-limited by GitHub, wait a few minutes and retry, or use a local manifest source.
- Fall back to the default repo by omitting the custom source.
Defensive patterns
Strategy: retry
Try / catch
async function fetchManifestWithRetry(url: string, maxRetries = 3): Promise<SkillsManifest> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetchManifest(url);
} catch (err) {
if (err instanceof Error && err.includes("HTTP 5") && i < maxRetries - 1) {
await new Promise((r) => setTimeout(r, 1000 * (i + 1)));
continue;
}
throw err;
}
}
throw new Error(`Failed after ${maxRetries} retries`);
} Prevention
- Verify the manifest URL is correct and accessible before relying on it.
- For GitHub raw URLs, use the default repo slug to avoid path errors.
- Have a local manifest fallback for when the remote is unreachable.
When it happens
Trigger: The manifest URL returns 404 (wrong path or repo), 403 (private repo without access), 5xx (server error), or any other non-2xx status. The fetch includes an AbortController with FETCH_TIMEOUT_MS, so a timeout triggers an AbortError (a different error), not this.
Common situations: Specifying a wrong owner/repo slug; the repo was renamed or made private; GitHub rate-limiting returns 403; the manifest path changed in a newer version of the skills repo; temporary CDN outage.
Related errors
- can't check skills freshness (GitHub manifest unreachable) —
- Malformed skills manifest from ${sourceLabel}
- Unknown skill(s): ${unknown.join(", ")}. Available: ${[...ma
- No skills manifest found at: ${source}
- selection endpoint returned ${response.status}
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/efc87afd778ddf70.
Report an issue: GitHub.