windmill-labs/windmill · error
This draft has a newer conflicting version on the server. Re
Error message
This draft has a newer conflicting version on the server. Resolve it here before opening a session.
What it means
After flushing the local draft, flushOrRefuse checks UserDraftDbSyncer for a conflict. If the server holds a newer version of the draft than the one just saved, it refuses to open the session so the user resolves the divergence in the editor first, preventing the session from starting on a stale draft.
Source
Thrown at frontend/src/lib/components/sessions/pageDrawerSession.ts:54
)
}
// Push the drawer's pending autosave and refuse to leave if it did not land: `flush`
// reports a failed or conflicting POST through its state rather than by throwing, so
// routing regardless would open the preview on the server's older draft while the drawer
// the user is looking at still holds the edit. The flush is the explicit kind, saving even
// with auto-save off: asking for a session is asking for the edit to come along.
async function flushOrRefuse(query: Parameters<typeof UserDraftDbSyncer.flush>[0]): Promise<void> {
// Before the save, not after: text that does not parse never reached the draft, so
// leaving now would open the session on the last value that did and drop the buffer
// with the drawer — and saving first would write that stale value on the way to
// refusing. The editors were materialised before this call, so the check is current.
if (anyEditorUnparseable()) {
throw new Error('This page has changes that are not valid JSON. Fix them first.')
}
await UserDraftDbSyncer.flush(query)
if (UserDraftDbSyncer.getConflict(query).conflict) {
throw new Error(
'This draft has a newer conflicting version on the server. Resolve it here before opening a session.'
)
}
const { state, failureMessage } = UserDraftDbSyncer.getState(query)
if (state === 'failed') {
throw new Error(
`Saving the latest draft failed (${failureMessage ?? 'unknown error'}). Retry before opening a session.`
)
}
}
// How each page addresses a row in its hash. Resources route theirs through an extra
// segment; every other page names the path directly.
const drawerHashFor = (pagePath: string, itemPath: string) =>
pagePath === RESOURCES_PATH ? `/resource/${itemPath}` : itemPath
/**
* Deep-link the row whose drawer just opened, so the location says what is on screen — aView on GitHub (pinned to e474e8803c)
Solutions
- Resolve the conflict in the editor UI (reload server version and reapply or discard local changes), then retry
- Reload the page to pick up the newer server draft before opening the session
- Coordinate with whoever edited the draft concurrently
Defensive patterns
Strategy: try-catch
Validate before calling
const { conflict } = UserDraftDbSyncer.getConflict(query)
if (conflict) return; // resolve before opening a session Try / catch
try { await openSession(query) } catch (e) { if (/conflicting version/.test(String(e))) promptConflictResolution(); else throw e } Prevention
- Avoid editing the same draft in multiple tabs
- Reload the draft before long editing sessions
- Resolve conflict banners promptly instead of ignoring them
When it happens
Trigger: flushOrRefuse(query) where getConflict(query).conflict is true — i.e. another tab/device or a concurrent edit wrote a newer draft version on the server while this page was open.
Common situations: The same draft edited in two browser tabs; a teammate edited the shared resource draft; the page was left open overnight while background sync updated the server copy.
Related errors
- Draft "${path}" changed externally since you last read it; i
- This page has changes that are not valid JSON. Fix them firs
- Saving the latest draft failed (${failureMessage ?? 'unknown
- This forked workspace '${workspaceId}' (${workspaceName}) al
- Failed to cache esbuild-wasm@${version} at ${destDir}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/6e261db952d6f933.
Report an issue: GitHub.