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

  1. Read the status in the message: 404 means the URL no longer exists — update it to the current SDK version path
  2. 403 from api.github.com usually means rate limit — add an Authorization header or wait and retry
  3. For 5xx/429, retry with backoff around fetchCached
  4. 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

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


AI-assisted analysis of sickn33/agentic-awesome-skills@58d857988f (2026-08-26). Data as JSON: /api/errors/634e58dfa62109cd. Report an issue: GitHub.