windmill-labs/windmill · error
Workspace "${workspace}" is not a fork — it has no parent wo
Error message
Workspace "${workspace}" is not a fork — it has no parent workspace to compare against. Use diff without 'against' to compare drafts vs deployed versions. What it means
The copilot diff tool's `against: 'parent'` mode compares a fork's deployed items against its parent workspace. `forkParentOrThrow` resolves the fork's parent id; if the workspace has none (it is not a fork), the tool throws this error explaining that `against` is fork-only and draft-vs-deployed diffing is the alternative. It is a mode-eligibility guard.
Source
Thrown at frontend/src/lib/components/copilot/chat/global/core.ts:6834
? `No drafts match your filters (${index.entries.length} draft(s) exist in the workspace).`
: 'No drafts in this workspace — nothing differs from the deployed state.'
: `${total} draft(s) vs deployed:`
const result = [summaryLine, ...lines, ...notes].join('\n')
toolCallbacks.setToolStatus(toolId, {
content: `Workspace diff: ${total} draft(s)`,
result
})
return result
}
function forkComparisonUnavailableMessage(parent: string): string {
return `The comparison with parent workspace "${parent}" is unavailable for this fork (created before comparison tracking existed).`
}
function forkParentOrThrow(workspace: string): string {
const parent = getForkParentWorkspaceId(workspace)
if (!parent) {
throw new Error(
`Workspace "${workspace}" is not a fork — it has no parent workspace to compare against. Use diff without 'against' to compare drafts vs deployed versions.`
)
}
return parent
}
function forkEntryLabel(e: ForkDiffEntryView): string {
const label = e.type === 'trigger' ? `${e.triggerKind} trigger` : (e.type ?? e.kind)
return `${label} "${e.path}"`
}
function formatForkIndexEntry(e: ForkDiffEntryView): string {
const name = forkEntryLabel(e)
const draftFlag = e.hasLocalDraft ? ' [+ local draft]' : ''
const aheadBehind = [
e.ahead > 0 ? `ahead ${e.ahead}` : undefined,
e.behind > 0 ? `behind ${e.behind}` : undefined
]View on GitHub (pinned to e474e8803c)
Solutions
- Drop the `against` argument to compare drafts vs deployed versions in the current workspace
- Switch to the actual fork workspace before using `against: 'parent'`
- To compare arbitrary workspaces, use the workspace UI/CLI rather than this tool
Example fix
// before
diff({ type: 'script', path: 'f/build', against: 'parent' }) // not a fork
// after
diff({ type: 'script', path: 'f/build' }) // draft vs deployed Defensive patterns
Strategy: validation
Validate before calling
const parent = getForkParentWorkspaceId(workspace)
if (!parent) throw new Error(`${workspace} is not a fork; use diff without 'against'` Type guard
function isFork(workspace: string): boolean {
return getForkParentWorkspaceId(workspace) != null
} Try / catch
try {
return await diffItem({ type, path, against: 'parent' })
} catch (e) {
if (String(e?.message).includes('is not a fork')) {
return await diffItem({ type, path }) // fall back to draft vs deployed
}
throw e
} Prevention
- Check the workspace is a fork before using against: 'parent'
- Only use the parent-comparison mode inside fork workspaces
- For non-fork workspaces, diff drafts vs deployed instead
When it happens
Trigger: Calling diff with `against: 'parent'` from a workspace that is not a fork (a normal top-level workspace), so `getForkParentWorkspaceId` returns falsy.
Common situations: Agents assuming every workspace has a parent; users trying to compare two arbitrary workspaces via `against`; running the fork-comparison flow in the original workspace instead of the fork.
Related errors
- path is required.
- file only applies to multi-file apps; ${entry.kind} "${path}
- Missing required arguments: ${required.join(", ")}. Use -d '
- file only applies to multi-file apps; ${type} "${path}" diff
- search requires a non-empty string.
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/e07f83571e8539ef.
Report an issue: GitHub.