mastra-ai/mastra · error · SkillInvocationError

skill_not_found

skill_not_found

Error message

skill_not_found

What it means

resolveSkillInvocation found the session, refreshed workspace skills, but skills.get(input.name) returned nothing or the skill has 'user-invocable': false. It throws SkillInvocationError('skill_not_found') with the skill name in the message.

Source

Thrown at mastracode/factory/src/skills/service.ts:71

  input: { resourceId: string; scope?: SkillInvocationInput['scope']; prompt: string },
): Promise<{ session: SkillSession; message: string }> {
  const session = (await controller.getSessionByResource(input.resourceId, input.scope)) as SkillSession | undefined;
  if (!session) throw new SkillInvocationError('session_not_found', 'Agent controller session not found.');
  return { session, message: input.prompt };
}

export async function resolveSkillInvocation(
  controller: Pick<AgentController<MastraCodeState>, 'getSessionByResource'>,
  input: SkillInvocationInput,
): Promise<{ session: SkillSession; skillName: string; message: string }> {
  const session = (await controller.getSessionByResource(input.resourceId, input.scope)) as SkillSession | undefined;
  if (!session) throw new SkillInvocationError('session_not_found', 'Agent controller session not found.');

  const skills = session.getWorkspace().skills;
  await skills?.maybeRefresh();
  const skill = await skills?.get(input.name);
  if (!skill || skill['user-invocable'] === false) {
    throw new SkillInvocationError('skill_not_found', `Skill not found: ${input.name}.`);
  }

  const args = input.arguments?.trim();
  const content = `${formatSkillActivation(skill)}${args ? `\n\nARGUMENTS: ${args}` : ''}`.trim();
  return {
    session,
    skillName: skill.name,
    message: `<skill name="${skill.name}">\n${escapeSkillBoundary(content)}\n</skill>`,
  };
}

export async function dispatchSkillInvocation(
  controller: Pick<AgentController<MastraCodeState>, 'getSessionByResource'>,
  input: SkillInvocationInput,
): Promise<{ skillName: string; message: string }> {
  const resolved = await resolveSkillInvocation(controller, input);
  await resolved.session.sendMessage({ content: resolved.message });
  return { skillName: resolved.skillName, message: resolved.message };

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check the skill name matches the skills directory/frontmatter exactly
  2. Ensure the skill frontmatter does not set 'user-invocable': false
  3. Call skills.maybeRefresh() or reload the workspace so newly added skills are visible
  4. List available skills from session.getWorkspace().skills to confirm the name

Example fix

// before
await resolveSkillInvocation(controller, { resourceId, name: 'Deploy-Skill' });
// after
const skills = session.getWorkspace().skills;
if (skills && !(await skills.get('deploy-skill'))) throw new Error('skill not installed in workspace');
await resolveSkillInvocation(controller, { resourceId, name: 'deploy-skill' });
Defensive patterns

Strategy: validation

Validate before calling

const skills = session.getWorkspace().skills;
await skills?.maybeRefresh();
const skill = await skills?.get(name);
if (!skill || skill['user-invocable'] === false) {
  throw new Error(`Skill "${name}" missing or not user-invocable in this workspace`);
}

Type guard

function isInvocableSkill(s: { 'user-invocable'?: boolean } | undefined): boolean {
  return s != null && s['user-invocable'] !== false;
}

Try / catch

try {
  await resolveSkillInvocation(controller, input);
} catch (e) {
  if (e instanceof SkillInvocationError && e.code === 'skill_not_found') {
    const available = await session.getWorkspace().skills?.list?.() ?? [];
    throw new Error(`Unknown skill "${input.name}". Available: ${available.join(', ')}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Invoking a skill whose name doesn't exist in the session workspace's skills directory, a typo in input.name, or invoking a skill marked 'user-invocable': false in its frontmatter.

Common situations: Skill added after the session's workspace snapshot (refresh pending or not persisted); skill name mismatch (directory name vs frontmatter name); skill intentionally restricted to agent-only invocation.

Related errors


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