windmill-labs/windmill · error · Error

Workspace '${forkWorkspaceId}' is not a fork (no parent_work

Error message

Workspace '${forkWorkspaceId}' is not a fork (no parent_workspace_id). You can only merge from a forked workspace.

What it means

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.)

Source

Thrown at cli/src/commands/workspace/merge.ts:277

    throw new Error("Not logged in. Please run 'wmill workspace add' first.");
  }

  const remote = workspace.remote;
  setClient(
    token,
    remote.endsWith("/") ? remote.substring(0, remote.length - 1) : remote
  );

  const forkWorkspaceId = workspace.workspaceId;

  // 2. Find parent workspace
  const userWorkspaces = await wmill.listUserWorkspaces();
  const forkEntry = userWorkspaces.workspaces?.find(
    (w) => w.id === forkWorkspaceId
  );

  if (!forkEntry?.parent_workspace_id) {
    throw new Error(
      `Workspace '${forkWorkspaceId}' is not a fork (no parent_workspace_id). ` +
        `You can only merge from a forked workspace.`
    );
  }

  const parentWorkspaceId = forkEntry.parent_workspace_id;
  log.info(
    `Fork: ${colors.bold(forkWorkspaceId)} → Parent: ${colors.bold(parentWorkspaceId)}`
  );

  // 3. Compare workspaces
  log.info("Comparing workspaces...");
  const comparison = await wmill.compareWorkspaces({
    workspace: parentWorkspaceId,
    targetWorkspaceId: forkWorkspaceId,
  });

  if (comparison.skipped_comparison) {

View on GitHub (pinned to e474e8803c)

Solutions

  1. Switch to the fork branch / workspace created via `wmill workspace fork` before merging
  2. Re-create the fork properly with `wmill workspace fork` so parent_workspace_id is set
  3. Verify with the workspace list on the backend that the target workspace is a fork

Example fix

// before
git checkout main && wmill workspace merge  # main is the parent
// after
git checkout my-fork && wmill workspace merge
Defensive patterns

Strategy: validation

Validate before calling

const { workspaces } = await wmill.listUserWorkspaces();
const fork = workspaces?.find(w => w.id === forkWorkspaceId);
if (!fork?.parent_workspace_id) {
  throw new Error(`${forkWorkspaceId} is not a fork; run wmill workspace fork to create one.`);
}

Type guard

function isForkEntry(w: { id: string; parent_workspace_id?: string | null }): w is { id: string; parent_workspace_id: string } {
  return !!w.parent_workspace_id;
}

Try / catch

try {
  await mergeWorkspaces(opts);
} catch (e) {
  if (e.message.includes('is not a fork')) {
    log.error('Create the fork with wmill workspace fork first.');
  } else throw e;
}

Prevention

When it happens

Trigger: `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.

Common situations: 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.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/babd44685127dc4c. Report an issue: GitHub.