mastra-ai/mastra · error · HTTPException

Skills API error: ${response.status} ${response.statusText}

Error message

Skills API error: ${response.status} ${response.statusText}

What it means

A 502 Bad Gateway raised by `searchSkillsSh` when the skills.sh registry API responds with a non-OK HTTP status. The server proxies search requests to SKILLS_SH_API_URL and surfaces upstream failures as 502 so clients know the problem is with the upstream registry, not their query.

Source

Thrown at packages/server/src/server/handlers/skills-sh-shared.ts:153

    displayName: string;
  }>;
  total: number;
  page?: number;
  pageSize?: number;
  totalPages?: number;
}

/** Search skills.sh by query string. Throws HTTPException on upstream failure. */
export async function searchSkillsSh({ q, limit }: { q: string; limit: number }): Promise<SkillsShSearchResult> {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), SEARCH_TIMEOUT_MS);

  try {
    const url = `${SKILLS_SH_API_URL}/api/skills?query=${encodeURIComponent(q)}&pageSize=${limit}`;
    const response = await fetch(url, { signal: controller.signal });

    if (!response.ok) {
      throw new HTTPException(502, {
        message: `Skills API error: ${response.status} ${response.statusText}`,
      });
    }

    const data = (await response.json()) as UpstreamSkillsList;
    return {
      query: q,
      searchType: 'query',
      skills: data.skills.map(s => ({ id: s.skillId, name: s.name, installs: s.installs, topSource: s.source })),
      count: data.total,
    };
  } finally {
    clearTimeout(timeoutId);
  }
}

/** Fetch the popular skills list from skills.sh. */
export async function getPopularSkillsSh({

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Retry the search after a short delay; upstream hiccups are often transient.
  2. Check skills.sh status directly (curl the API URL) to confirm an outage.
  3. Verify outbound network/egress from the Mastra server host (proxy, firewall, DNS).
  4. Inspect the status/statusText in the message (429 vs 500) to decide between backoff and outage handling.

Example fix

// before
const results = await searchSkills('react'); // throws 502 on upstream failure
// after
async function searchSkillsSafe(q) {
  try { return await searchSkills(q); }
  catch (e) {
    if (e?.status === 502) { await new Promise(r => setTimeout(r, 1000)); return searchSkills(q); }
    throw e;
  }
}
Defensive patterns

Strategy: retry

Try / catch

try {
  const results = await searchSkills(q);
  return results;
} catch (e) {
  if (e instanceof MastraClientError && e.status === 502) {
    // upstream skills.sh failure — retry with backoff or serve cached results
    await new Promise(r => setTimeout(r, 1000));
    return searchSkills(q);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the builder registry search route while skills.sh is down, rate-limiting (429), returning 5xx, or an intermittent proxy/firewall altering the response.

Common situations: skills.sh outage or maintenance; network egress blocked from a self-hosted server; upstream rate limits under heavy search traffic; DNS/CDN errors producing non-OK statuses.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/b3b8e5de290c64de. Report an issue: GitHub.