{"record":{"id":"e70abe772b626aec","repo":"ramensoftware/windhawk","slug":"mod-id-must-only-contain-the-following-characters-0-9-a-z","errorCode":null,"errorMessage":"Mod id must only contain the following characters: 0-9, a-z, and a hyphen (-)","messagePattern":"Mod id must only contain the following characters: 0-9, a-z, and a hyphen \\(-\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/windhawk-vscode/src/utils/editorWorkspaceUtils.ts","lineNumber":113,"sourceCode":"\t\t\t// Ignore if file doesn't exist.\n\t\t\tif (e.code !== 'ENOENT') {\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\n\t\tthis.initializeEditorSettings(compileFlags);\n\n\t\tif (modSourceFromDrafts) {\n\t\t\t// Write the new content after initializing, so that git won't stage the draft changes.\n\t\t\tfs.writeFileSync(this.getFilePath('mod.wh.cpp'), modSourceFromDrafts);\n\t\t}\n\t}\n\n\t// Reject an id that would escape the drafts folder before it reaches any of\n\t// the filesystem calls below.\n\tprivate getDraftPath(modId: string) {\n\t\tif (!isValidModId(modId)) {\n\t\t\tthrow new Error('Mod id must only contain the following characters: 0-9, a-z, and a hyphen (-)');\n\t\t}\n\n\t\treturn path.join(this.getDraftsPath(), modId + '.wh.cpp');\n\t}\n\n\tpublic saveModToDrafts(modId: string) {\n\t\tconst modSourcePath = this.getDraftPath(modId);\n\t\tfs.mkdirSync(this.getDraftsPath(), { recursive: true });\n\t\tfs.copyFileSync(this.getFilePath('mod.wh.cpp'), modSourcePath);\n\t}\n\n\tpublic loadModFromDrafts(modId: string) {\n\t\tconst modSourcePath = this.getDraftPath(modId);\n\t\tif (fs.existsSync(modSourcePath)) {\n\t\t\treturn fs.readFileSync(modSourcePath, 'utf8');\n\t\t}\n\n\t\treturn null;","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/ramensoftware/windhawk/blob/61d99ed8e182e1af1b60109612b6763ad1b4b74e/src/windhawk-vscode/src/utils/editorWorkspaceUtils.ts#L95-L131","documentation":"getDraftPath joins a mod id onto the drafts directory path, so an invalid id could escape that folder (e.g. via '../' or illegal filename characters). It validates the id with isValidModId — only digits 0-9, lowercase a-z, and hyphens are allowed — and throws this error when validation fails.","triggerScenarios":"Calling getDraftPath (via modSourcePath, saveModToDrafts, loadModFromDrafts, etc.) with a mod id containing uppercase letters, underscores, spaces, dots, slashes, or non-ASCII characters — e.g. a mod named 'My_Mod' or 'pkg/submod'.","commonSituations":"Manually editing a mod's id in metadata to a CamelCase or underscore name; importing/pasting a mod id from another source (Windhawk mods use lowercase-hyphen ids like 'taskbar-clock-customization'); path-traversal strings in adversarial input.","solutions":["Rename the mod id to lowercase alphanumeric with hyphens only (e.g. 'My_Mod' → 'my-mod').","Validate the id before calling modSourcePath/saveModToDrafts, e.g. /^[0-9a-z-]+$/.test(modId).","If the id comes from user input or metadata files, sanitize by lowercasing and replacing invalid characters with hyphens before use.","For a mod id that legitimately needs other characters, contact the Windhawk project — the format is enforced by design to keep drafts paths safe."],"exampleFix":"// before\nconst p = workspaceUtils.modSourcePath('My_Mod');\n// after\nconst id = 'My_Mod'.toLowerCase().replace(/[^0-9a-z-]/g, '-');\nconst p = workspaceUtils.modSourcePath(id);","handlingStrategy":"validation","validationCode":"const isValidModId = (id) => /^[0-9a-z-]+$/.test(id);\nif (!isValidModId(modId)) throw new Error(`Invalid mod id: ${modId}`);","typeGuard":null,"tryCatchPattern":"try {\n  const p = utils.modSourcePath(modId);\n} catch (e) {\n  if (e.message.startsWith('Mod id must only contain')) {\n    showInvalidIdMessage(modId);\n  }\n}","preventionTips":["Validate mod ids against /^[0-9a-z-]+$/ at every entry point (UI input, metadata parse, CLI arg).","Normalize external ids: lowercase and replace invalid characters with hyphens before use.","Never build draft paths from raw user-supplied strings without the id check."],"tags":["validation","identifier","path-safety"],"backgroundTag":"invalid-identifier-format","analyzedSha":"61d99ed8e182e1af1b60109612b6763ad1b4b74e","analyzedAt":"2026-09-12T14:02:41.115Z","contentChangedAt":"2026-09-12T14:02:41.115Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}