{"record":{"id":"2414dc90098b7b32","repo":"Yeachan-Heo/oh-my-codex","slug":"ask-agent-prompt-role-normalizedrole-not","errorCode":null,"errorMessage":"[ask] --agent-prompt role \"${normalizedRole}\" not found in ${promptsDir}.${availableSuffix}","messagePattern":"\\[ask\\] --agent-prompt role \"(.+?)\" not found in (.+?)\\.(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/ask.ts","lineNumber":80,"sourceCode":"  if (!SAFE_ROLE_PATTERN.test(normalizedRole)) {\n    throw new Error(`[ask] invalid --agent-prompt role \"${role}\". Expected lowercase role names like \"executor\" or \"test-engineer\".`);\n  }\n\n  if (!existsSync(promptsDir)) {\n    throw new Error(`[ask] prompts directory not found: ${promptsDir}. Run \"omx setup\" to install prompts.`);\n  }\n\n  const promptPath = join(promptsDir, `${normalizedRole}.md`);\n  if (!existsSync(promptPath)) {\n    const files = await readdir(promptsDir).catch(() => [] as string[]);\n    const availableRoles = files\n      .filter((file) => file.endsWith('.md'))\n      .map((file) => file.slice(0, -3))\n      .sort();\n    const availableSuffix = availableRoles.length > 0\n      ? ` Available roles: ${availableRoles.join(', ')}.`\n      : '';\n    throw new Error(`[ask] --agent-prompt role \"${normalizedRole}\" not found in ${promptsDir}.${availableSuffix}`);\n  }\n\n  const content = (await readFile(promptPath, 'utf-8')).trim();\n  if (!content) {\n    throw new Error(`[ask] --agent-prompt role \"${normalizedRole}\" is empty: ${promptPath}`);\n  }\n\n  return content;\n}\n\nexport function parseAskArgs(args: readonly string[]): ParsedAskArgs {\n  const [providerRaw, ...rest] = args;\n  const provider = (providerRaw || '').toLowerCase();\n\n  if (!provider || !ASK_PROVIDER_SET.has(provider)) {\n    throw askUsageError(`Invalid provider \"${providerRaw || ''}\". Expected one of: ${ASK_PROVIDERS.join(', ')}.`);\n  }\n","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/ask.ts#L62-L98","documentation":"The prompts directory exists but contains no `<role>.md` file for the requested role. The error lists available roles (all .md filenames minus extension, sorted) so the user can immediately pick a valid one.","triggerScenarios":"Passing `--agent-prompt architect` when only executor.md and test-engineer.md exist in the prompts directory; renamed prompt files; stale role names after prompt set changes.","commonSituations":"Role renamed between omx versions; team-specific prompt sets that don't include the requested role; typos in the role name that still pass the safe-character regex.","solutions":["Pick from the 'Available roles:' list printed in the error message","Check for the exact file: `ls <promptsDir>` and use the basename without .md","Update to a version that ships the role you need, or create `<role>.md` in the prompts dir yourself","Fix typos in the role name"],"exampleFix":"# before\nomx ask --agent-prompt architech \"task\"\n# after\nomx ask --agent-prompt executor \"task\"","handlingStrategy":"validation","validationCode":"import { existsSync, readdirSync } from 'node:fs';\nconst available = readdirSync(promptsDir).filter(f => f.endsWith('.md')).map(f => f.slice(0,-3));\nif (!available.includes(role)) throw new Error(`role '${role}' not in ${available.join(', ')}`);","typeGuard":"const isKnownRole = (roles: string[]) => (r: string): r is string => roles.includes(r);","tryCatchPattern":"catch (e) { const m = /Available roles: (.+)\\./.exec(String(e)); if (m) suggestRoles(m[1].split(', ')); else throw e; }","preventionTips":["List available roles before prompting users for one","Sync role names with prompt file names in code review","Pin prompt sets in version control"],"tags":["ask","prompts","file-not-found","role"],"backgroundTag":"file-not-found","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}