{"record":{"id":"d94d319659cf2943","repo":"windmill-labs/windmill","slug":"fork-workspace-id-id-is-invalid-reason","errorCode":null,"errorMessage":"Fork workspace id \\`${id}\\` is invalid: ${reason}. Choose a shorter or simpler name/id.","messagePattern":"Fork workspace id \\\\`(.+?)\\\\` is invalid: (.+?)\\. Choose a shorter or simpler name/id\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/workspace/fork.ts","lineNumber":500,"sourceCode":" */\nfunction branchToForkId(branch: string): string {\n  const slug = branch\n    .replace(/[^a-zA-Z0-9_-]+/g, \"-\")\n    .replace(/^-+|-+$/g, \"\")\n    .slice(0, MAX_FORK_ID_SLUG)\n    .replace(/-+$/g, \"\"); // re-trim if the cut landed on a dash\n  return slug || \"fork\";\n}\n\n/**\n * Mirror the backend `validate_fork_workspace_id` so an invalid id fails fast\n * — before `existsWorkspace`, datatable cloning (which creates real per-fork\n * Postgres databases), and branch creation, none of which get cleaned up on a\n * late backend rejection. `id` is the full `wm-fork-<slug>` workspace id.\n */\nfunction validateForkWorkspaceId(id: string): void {\n  const reject = (reason: string): never => {\n    throw new Error(\n      `Fork workspace id \\`${id}\\` is invalid: ${reason}. Choose a shorter or simpler name/id.`,\n    );\n  };\n  if (id.length > 50) {\n    reject(`too long (${id.length} chars; max 50 including the \\`${WM_FORK_PREFIX}-\\` prefix)`);\n  }\n  if (id.endsWith(\".\")) reject(\"cannot end with '.'\");\n  if (id.endsWith(\".lock\")) reject(\"cannot end with '.lock'\");\n  if (id.includes(\"..\")) reject(\"cannot contain '..'\");\n  if (id.includes(\"@{\")) reject(\"cannot contain '@{'\");\n  if (id.includes(\"//\")) reject(\"cannot contain '//'\");\n  for (const ch of id) {\n    if (\":~^?*[\\\\ \".includes(ch)) reject(`contains forbidden character '${ch}'`);\n    const code = ch.charCodeAt(0);\n    if (code < 0x20 || code === 0x7f) reject(\"contains a control character\");\n  }\n  for (const component of id.split(\"/\")) {\n    if (component.startsWith(\".\")) reject(\"a path component cannot start with '.'\");","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/workspace/fork.ts#L482-L518","documentation":"`validateForkWorkspaceId` pre-validates the generated `wm-fork-<slug>` workspace id (max 50 chars, simple slug) before any backend call. This early rejection exists because later steps (existsWorkspace, datatable cloning, branch creation) are not cleaned up on a late backend rejection.","triggerScenarios":"The workspace name given to `wmill workspace fork` produces an id longer than 50 characters or containing characters outside the allowed slug set; `reject` is called with the specific reason.","commonSituations":"Long descriptive fork names like `wm-fork-my-very-long-experimental-feature-branch-2026` exceeding the limit; names with spaces or uppercase letters.","solutions":["Choose a shorter fork workspace name so `wm-fork-<name>` stays ≤ 50 chars","Use only lowercase alphanumeric and dash characters in the name","Re-run `wmill workspace fork` with the simplified id"],"exampleFix":"// before\nwmill workspace fork --name my-extremely-long-experimental-feature-branch-name-2026\n// after\nwmill workspace fork --name exp-feature","handlingStrategy":"validation","validationCode":"const id = `${WM_FORK_PREFIX}-${slug(name)}`;\nif (id.length > 50 || !/^[a-z0-9-]+$/.test(id)) {\n  throw new Error(`fork id ${id} invalid; shorten or simplify`);\n}","typeGuard":"function isValidForkId(id: string): boolean {\n  return id.length <= 50 && /^wm-fork-[a-z0-9-]+$/.test(id);\n}","tryCatchPattern":"try {\n  validateForkWorkspaceId(id);\n} catch (e) {\n  log.error(e.message); // includes the specific reason (too long, bad chars)\n}","preventionTips":["Keep fork names short (well under 50 chars including the wm-fork- prefix)","Use lowercase alphanumeric names with dashes","Validate the generated id before invoking fork creation to avoid half-created state"],"tags":["cli","validation","naming","workspace-fork"],"backgroundTag":"workspace-id-validation-failed","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}