{"record":{"id":"e73c5cbf6bcf0538","repo":"Fission-AI/OpenSpec","slug":"invalid-change-name-changename-lookuperror","errorCode":null,"errorMessage":"Invalid change name '${changeName}': ${lookupError}","messagePattern":"Invalid change name '(.+?)': (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/commands/workflow/shared.ts","lineNumber":207,"sourceCode":"): Promise<string> {\n  // Hints must stay pasteable: callers with a selected store pass a\n  // store-carrying hint so following it lands in the same root.\n  const newChangeHint = hints.newChangeHint ?? 'openspec new change <name>';\n\n  if (!changeName) {\n    const available = await getAvailableChanges(projectRoot, changesDir);\n    if (available.length === 0) {\n      throw new Error(`No changes found. Create one with: ${newChangeHint}`);\n    }\n    throw new Error(\n      `Missing required option --change. Available changes:\\n  ${available.join('\\n  ')}`\n    );\n  }\n\n  // Validate change name format to prevent path traversal\n  const lookupError = validateChangeLookupName(changeName);\n  if (lookupError) {\n    throw new Error(`Invalid change name '${changeName}': ${lookupError}`);\n  }\n\n  // Check directory existence directly\n  const changePath = path.join(changesDir, changeName);\n  const exists = fs.existsSync(changePath) && fs.statSync(changePath).isDirectory();\n\n  if (!exists) {\n    const available = await getAvailableChanges(projectRoot, changesDir);\n    if (available.length === 0) {\n      throw new Error(\n        `Change '${changeName}' not found. No changes exist. Create one with: ${newChangeHint}`\n      );\n    }\n    throw new Error(\n      `Change '${changeName}' not found. Available changes:\\n  ${available.join('\\n  ')}`\n    );\n  }\n","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/Fission-AI/OpenSpec/blob/6926ccb18afa4ff621112813e9968334576ee11a/src/commands/workflow/shared.ts#L189-L225","documentation":"Path-traversal guard in validateChangeExists: validateChangeLookupName rejected the change name's format (e.g. it contains slashes or other unsafe characters), so it is never joined onto the changes directory. The embedded lookupError text names the specific rule violated.","triggerScenarios":"Thrown at src/commands/workflow/shared.ts:207 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Use a plain kebab-case change name without path separators or traversal segments","Check the appended reason for the exact rule violated"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"6926ccb18afa4ff621112813e9968334576ee11a","analyzedAt":"2026-08-25T12:29:45.729Z","contentChangedAt":"2026-08-25T12:29:45.729Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}