{"record":{"id":"e4c831de2f7363d6","repo":"Yeachan-Heo/oh-my-codex","slug":"ask-invalid-agent-prompt-role-role-expec","errorCode":null,"errorMessage":"[ask] invalid --agent-prompt role \"${role}\". Expected lowercase role names like \"executor\" or \"test-engineer\".","messagePattern":"\\[ask\\] invalid --agent-prompt role \"(.+?)\"\\. Expected lowercase role names like \"executor\" or \"test-engineer\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/ask.ts","lineNumber":63,"sourceCode":"      const parsed = JSON.parse(readFileSync(scopePath, 'utf-8')) as Partial<{ scope: string }>;\n      if (parsed.scope === 'project' || parsed.scope === 'project-local') {\n        return join(cwd, '.codex', 'prompts');\n      }\n    }\n  } catch {\n    // Ignore malformed persisted scope and fall back to user prompts.\n  }\n\n  return codexPromptsDir();\n}\n\nasync function resolveAgentPromptContent(\n  role: string,\n  promptsDir: string,\n): Promise<string> {\n  const normalizedRole = role.trim().toLowerCase();\n  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  }","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/ask.ts#L45-L81","documentation":"`omx ask --agent-prompt <role>` validates the role against SAFE_ROLE_PATTERN (lowercase alphanumeric/hyphen names) before touching the filesystem, as a path-traversal guard. Any role containing uppercase, slashes, dots, or other characters fails immediately with this error.","triggerScenarios":"Passing `--agent-prompt Executor`, `--agent-prompt ../secrets`, `--agent-prompt test_engineer` (underscore if not allowed), or any role that does not match the safe-name regex.","commonSituations":"Users typing natural role names with capitals or spaces; scripts interpolating user input into the flag; attempted path traversal via the role argument.","solutions":["Use lowercase hyphenated role names, e.g. `--agent-prompt executor` or `--agent-prompt test-engineer`","Trim whitespace and remove special characters from the role string in calling scripts","Sanitize user-supplied roles before passing them: lowercase and strip invalid characters"],"exampleFix":"# before\nomx ask --agent-prompt \"Test Engineer\" \"do work\"\n# after\nomx ask --agent-prompt test-engineer \"do work\"","handlingStrategy":"validation","validationCode":"const SAFE = /^[a-z0-9]+(-[a-z0-9]+)*$/;\nconst role = String(rawRole).trim().toLowerCase();\nif (!SAFE.test(role)) throw new Error('role must be lowercase-hyphen');","typeGuard":"const isSafeRole = (r: string): r is string => /^[a-z0-9]+(-[a-z0-9]+)*$/.test(r.trim());","tryCatchPattern":"catch (e) { if (/invalid --agent-prompt role/.test(String(e))) { promptUserForValidRole(); } else throw e; }","preventionTips":["Normalize roles to lowercase-hyphen before passing","Never interpolate raw user input into --agent-prompt","Maintain a typed union of known roles in your app"],"tags":["validation","cli","ask","path-traversal","argument-parsing"],"backgroundTag":"invalid-argument-format","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}