mastra-ai/mastra · error · HTTPException

No content available for skill "${skillName}"

Error message

No content available for skill "${skillName}"

What it means

After a successful skills.sh response, previewSkillsSh extracts content from the JSON body (instructions or raw fields). If both are empty/missing, the skill technically exists but has no usable content, so the route throws HTTPException 404. This prevents returning an empty preview to the UI.

Source

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

    const url = `${SKILLS_SH_API_URL}/api/skills/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(skillName)}/content`;
    const response = await fetch(url, { signal: controller.signal });

    if (!response.ok) {
      if (response.status === 404) {
        throw new HTTPException(404, {
          message: `Could not find skill "${skillName}" for ${owner}/${repo}`,
        });
      }
      throw new HTTPException(502, {
        message: `Skills API error: ${response.status} ${response.statusText}`,
      });
    }

    const data = (await response.json()) as { instructions?: string; raw?: string };
    const content = data.instructions || data.raw || '';

    if (!content) {
      throw new HTTPException(404, {
        message: `No content available for skill "${skillName}"`,
      });
    }

    return { content };
  } finally {
    clearTimeout(timeoutId);
  }
}

/**
 * Fetch the full file tree for a skill from the skills.sh files endpoint.
 * Returns null when the skill doesn't exist (404). Throws on other upstream
 * errors. Caller is responsible for validating each returned file path with
 * `assertSafeFilePath` before writing to disk.
 */
export async function fetchSkillFiles(
  owner: string,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Verify the skill's SKILL.md/content is actually published on skills.sh
  2. Re-publish or push the skill content to the source repo and let skills.sh re-index
  3. If the API shape changed, update the data.instructions || data.raw extraction to match the new field names
  4. Surface a 'skill has no content' state in the UI instead of an empty preview

Example fix

// before
const content = data.instructions || data.raw || '';
// after (also check alternate fields)
const content = data.instructions || data.raw || data.content || '';
Defensive patterns

Strategy: fallback

Validate before calling

// Can't be checked before the call; validate after receiving the payload
const hasContent = (d: { instructions?: string; raw?: string }) => Boolean(d.instructions || d.raw);

Type guard

function hasSkillContent(d: unknown): d is { instructions: string } | { raw: string } {
  const o = d as any;
  return typeof o === 'object' && o !== null && (typeof o.instructions === 'string' && o.instructions.length > 0 || typeof o.raw === 'string' && o.raw.length > 0);
}

Try / catch

let preview;
try {
  preview = await api.previewSkill(owner, repo, name);
} catch (e) {
  if (isEmptyContentError(e)) preview = { content: null, note: 'Skill published but has no content' };
  else throw e;
}

Prevention

When it happens

Trigger: Previewing a skill whose skills.sh content payload contains neither 'instructions' nor 'raw' (or both are empty strings); the upstream 200 response has no content fields.

Common situations: Skill published with an empty SKILL.md; skills.sh indexing a repo but failing to extract content; skill metadata present but content not yet generated; API schema change upstream.

Related errors


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