sickn33/agentic-awesome-skills · error
HTTP ${response.status}: ${response.statusText}
Error message
HTTP ${response.status}: ${response.statusText} What it means
fetchCached wraps Node's global fetch with an ETag/disk cache under scripts/.cache; after a non-304 response with response.ok false it throws a plain Error carrying the HTTP status and statusText. It surfaces upstream failures (404, 403, 5xx) from the remote endpoints the expo-cicd-workflows scripts fetch.
Source
Thrown at skills/expo-cicd-workflows/scripts/fetch.js:37
// Make request, with conditional If-None-Match if we have an ETag.
// Cache-Control: max-age=0 overrides Node's default 'no-cache' to allow 304 responses.
const response = await fetch(url, {
headers: {
'Cache-Control': 'max-age=0',
...(cached?.etag && { 'If-None-Match': cached.etag }),
},
});
if (response.status === 304 && cached) {
// Refresh expiration and return cached data
const entry = { ...cached, expires: getExpires(response.headers) };
await saveCacheEntry(cacheFile, entry);
return cached.data;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const etag = response.headers.get('etag');
const data = await response.text();
const expires = getExpires(response.headers);
await saveCacheEntry(cacheFile, { url, etag, expires, data });
return data;
}
function hashUrl(url) {
return createHash('sha256').update(url).digest('hex').slice(0, 16);
}
async function loadCacheEntry(cacheFile) {
try {
return JSON.parse(await readFile(cacheFile, 'utf-8'));View on GitHub (pinned to 58d857988f)
Solutions
- Read the status in the message: 404 means the URL no longer exists — update it to the current SDK version path
- 403 from api.github.com usually means rate limit — add an Authorization header or wait and retry
- For 5xx/429, retry with backoff around fetchCached
- Check for stale entries in skills/expo-cicd-workflows/scripts/.cache only if responses look wrong; errors are never cached
Example fix
// before
const yaml = await fetchCached(workflowUrl);
// after
async function fetchWithRetry(url, tries = 3) {
for (let i = 0; i < tries; i++) {
try { return await fetchCached(url); }
catch (e) {
if (i === tries - 1 || !/^HTTP (5\d\d|429)/.test(e.message)) throw e;
await new Promise(r => setTimeout(r, 2000 * 2 ** i));
}
}
}
const yaml = await fetchWithRetry(workflowUrl); Defensive patterns
Strategy: retry
Validate before calling
const u = new URL(target);
if (!/^https?:$/.test(u.protocol)) throw new Error(`Unsupported protocol: ${u.protocol}`); Try / catch
for (let i = 0; i < 3; i++) {
try { return await fetchCached(url); }
catch (e) {
if (!/^HTTP /.test(e.message)) throw e; // network error: fail fast
if (!/^HTTP (5\d\d|429)/.test(e.message) || i === 2) throw e; // other 4xx: permanent
await new Promise(r => setTimeout(r, 1000 * 2 ** i));
}
} Prevention
- Keep template/asset URLs version-pinned; update on SDK upgrades
- Authenticate GitHub API calls to raise rate limits
- Treat 5xx/429 as retryable and 403/404 as permanent
- Branch on the status code embedded in the message
When it happens
Trigger: Calling fetchCached(url) where the server returns 404 (deleted or moved workflow template URL), 403 (GitHub API rate limit without a token), or 5xx (service outage). Cache hits and 304 responses never throw.
Common situations: Expo SDK workflow URLs that changed between versions; unauthenticated GitHub API rate limits when polling many template refs; transient CI-provider 5xx during builds; stale hardcoded URLs.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- HTTP ${res.status}
- HTTP error: ${response.status}
- HTTP ${response.status}
- HTTP ${response.status}
- HTTP ${posts.status}
AI-assisted analysis of sickn33/agentic-awesome-skills@58d857988f (2026-08-26).
Data as JSON: /api/errors/634e58dfa62109cd.
Report an issue: GitHub.