{"record":{"id":"babd44685127dc4c","repo":"windmill-labs/windmill","slug":"workspace-forkworkspaceid-is-not-a-fork-no-p","errorCode":null,"errorMessage":"Workspace '${forkWorkspaceId}' is not a fork (no parent_workspace_id). You can only merge from a forked workspace.","messagePattern":"Workspace '(.+?)' is not a fork \\(no parent_workspace_id\\)\\. You can only merge from a forked workspace\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/workspace/merge.ts","lineNumber":277,"sourceCode":"    throw new Error(\"Not logged in. Please run 'wmill workspace add' first.\");\n  }\n\n  const remote = workspace.remote;\n  setClient(\n    token,\n    remote.endsWith(\"/\") ? remote.substring(0, remote.length - 1) : remote\n  );\n\n  const forkWorkspaceId = workspace.workspaceId;\n\n  // 2. Find parent workspace\n  const userWorkspaces = await wmill.listUserWorkspaces();\n  const forkEntry = userWorkspaces.workspaces?.find(\n    (w) => w.id === forkWorkspaceId\n  );\n\n  if (!forkEntry?.parent_workspace_id) {\n    throw new Error(\n      `Workspace '${forkWorkspaceId}' is not a fork (no parent_workspace_id). ` +\n        `You can only merge from a forked workspace.`\n    );\n  }\n\n  const parentWorkspaceId = forkEntry.parent_workspace_id;\n  log.info(\n    `Fork: ${colors.bold(forkWorkspaceId)} → Parent: ${colors.bold(parentWorkspaceId)}`\n  );\n\n  // 3. Compare workspaces\n  log.info(\"Comparing workspaces...\");\n  const comparison = await wmill.compareWorkspaces({\n    workspace: parentWorkspaceId,\n    targetWorkspaceId: forkWorkspaceId,\n  });\n\n  if (comparison.skipped_comparison) {","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/commands/workspace/merge.ts#L259-L295","documentation":"The merge flow fetches the user's workspaces from the backend and looks up the resolved fork id. If the workspace exists but has no `parent_workspace_id`, it is not a fork, and merging from it is meaningless, so the command throws. (It also throws earlier if the id isn't found at all.)","triggerScenarios":"`wmill workspace merge` while the resolved workspace id corresponds to a normal (non-fork) workspace on the backend — e.g. merging from a base workspace or an id without a recorded parent.","commonSituations":"Running merge while on the main/base branch mapped to the parent workspace; fork created out-of-band (workspace created manually, not via `wmill workspace fork`) so no parent link exists; fork relationship removed server-side.","solutions":["Switch to the fork branch / workspace created via `wmill workspace fork` before merging","Re-create the fork properly with `wmill workspace fork` so parent_workspace_id is set","Verify with the workspace list on the backend that the target workspace is a fork"],"exampleFix":"// before\ngit checkout main && wmill workspace merge  # main is the parent\n// after\ngit checkout my-fork && wmill workspace merge","handlingStrategy":"validation","validationCode":"const { workspaces } = await wmill.listUserWorkspaces();\nconst fork = workspaces?.find(w => w.id === forkWorkspaceId);\nif (!fork?.parent_workspace_id) {\n  throw new Error(`${forkWorkspaceId} is not a fork; run wmill workspace fork to create one.`);\n}","typeGuard":"function isForkEntry(w: { id: string; parent_workspace_id?: string | null }): w is { id: string; parent_workspace_id: string } {\n  return !!w.parent_workspace_id;\n}","tryCatchPattern":"try {\n  await mergeWorkspaces(opts);\n} catch (e) {\n  if (e.message.includes('is not a fork')) {\n    log.error('Create the fork with wmill workspace fork first.');\n  } else throw e;\n}","preventionTips":["Create forks only via wmill workspace fork so parent_workspace_id is recorded","Run merge from the fork branch, not the base branch","Inspect listUserWorkspaces before merging"],"tags":["cli","workspace-merge","fork","backend-state"],"backgroundTag":"workspace-not-a-fork","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"}