{"record":{"id":"92611e58d554c3a4","repo":"mastra-ai/mastra","slug":"skill-not-found-invocation-skillname","errorCode":null,"errorMessage":"Skill not found: ${invocation.skillName}.","messagePattern":"Skill not found: (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/rules/start-coordinator.ts","lineNumber":76,"sourceCode":"type FactoryController = AgentController<MastraCodeState>;\ntype FactorySession = Awaited<ReturnType<FactoryController['createSession']>>;\n\nfunction escapeSkillBoundary(value: string): string {\n  return value.replaceAll('</skill>', '&lt;/skill&gt;');\n}\n\nasync function resolveKickoffMessage(\n  session: FactorySession,\n  invocation: FactoryStartRequest['invocation'],\n): Promise<string | null> {\n  if (!invocation) return null;\n  if (invocation.type === 'prompt') return invocation.prompt;\n\n  const skills = session.getWorkspace()?.skills;\n  await skills?.maybeRefresh();\n  const skill = await skills?.get(invocation.skillName);\n  if (!skill || skill['user-invocable'] === false) {\n    throw new Error(`Skill not found: ${invocation.skillName}.`);\n  }\n  const args = invocation.arguments.trim();\n  const content = `${formatSkillActivation(skill)}${args ? `\\n\\nARGUMENTS: ${args}` : ''}`.trim();\n  return `<skill name=\"${skill.name}\">\\n${escapeSkillBoundary(content)}\\n</skill>`;\n}\n\nasync function resolveSourceSession(\n  storage: SourceControlStorageHandle,\n  request: FactoryStartRequest,\n): Promise<SourceControlSession> {\n  const session = await storage.sessions.getBySessionId(request.sessionId);\n  if (!session || session.orgId !== request.orgId || session.userId !== request.userId) {\n    throw new Error('Factory session not found');\n  }\n  const projectRepository = await storage.projectRepositories.get({\n    orgId: request.orgId,\n    id: session.projectRepositoryId,\n  });","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/rules/start-coordinator.ts#L58-L94","documentation":"Thrown by resolveKickoffMessage when a factory start request includes a skill-based kickoff invocation whose skill name cannot be resolved from the session workspace, or the resolved skill is not user-invocable. After an optional refresh, the workspace skills store returns nothing for `invocation.skillName`, or the skill record carries `user-invocable: false`, so building the `<skill>` kickoff message is refused rather than silently starting with a broken kickoff.","triggerScenarios":"Calling factory start (prepare/triage/plan/first/replay paths that resolve a kickoff message) with an invocation of type 'skill' where (a) the session workspace has no skills store (skills is undefined), (b) no skill exists under invocation.skillName (typo, renamed/deleted skill, not yet synced to the workspace), or (c) the skill exists but has frontmatter `user-invocable: false` so it may only be invoked by the agent, not by a user prompt.","commonSituations":"Developer renamed a SKILL.md directory or name field but old automations still pass the old name; the skill lives in a different repo/directory than the session workspace so it is never indexed; the skill is intentionally internal-only (user-invocable: false) but wired into a user-facing kickoff; a stale workspace cache that maybeRefresh fails to update.","solutions":["Verify the skill exists in the session workspace under the exact name in invocation.skillName (check the skill's frontmatter `name` matches, names are case-sensitive).","If the skill should be user-triggerable, set or remove the `user-invocable: false` frontmatter flag in the skill's SKILL.md.","Ensure the skill file is present in the workspace directory for this session; move/copy the skill or start the session in the correct workspace.","If the skill is agent-only by design, change the kickoff to a 'prompt'-type invocation with inline instructions instead of a skill reference."],"exampleFix":"// before\nawait factory.start({\n  sessionId,\n  kickoff: { type: 'skill', skillName: 'code-review', arguments: '' } // skill is user-invocable: false\n});\n\n// after (either fix works)\nawait factory.start({\n  sessionId,\n  kickoff: { type: 'skill', skillName: 'code-review', arguments: '' } // after removing `user-invocable: false` from SKILL.md\n});\n// or\nawait factory.start({\n  sessionId,\n  kickoff: { type: 'prompt', prompt: 'Run the code-review skill on the diff.' }\n});","handlingStrategy":"validation","validationCode":"const skills = session.getWorkspace()?.skills;\nawait skills?.maybeRefresh();\nconst skill = await skills?.get(invocation.skillName);\nif (!skill || skill['user-invocable'] === false) {\n  throw new Error(`Cannot start: skill '${invocation.skillName}' is missing or not user-invocable in this workspace.`);\n}","typeGuard":"function isInvocableSkill(s: unknown): s is { name: string; 'user-invocable': true } {\n  return typeof s === 'object' && s !== null && 'name' in s && (s as any)['user-invocable'] !== false;\n}","tryCatchPattern":"try {\n  await factory.prepare(request);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Skill not found:')) {\n    console.error(`Kickoff skill '${request.kickoff.skillName}' not found in workspace; check name and user-invocable flag.`);\n  }\n  throw err;\n}","preventionTips":["Keep skill frontmatter `name` in sync wherever the skill is referenced in automations.","List the workspace skills (and their user-invocable flags) before wiring a skill-based kickoff.","Prefer prompt-type kickoffs for agent-only skills."],"tags":["skill","workspace","configuration","lookup-failed"],"backgroundTag":"resource-not-found","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}