windmill-labs/windmill · error
This page has changes that are not valid JSON. Fix them firs
Error message
This page has changes that are not valid JSON. Fix them first.
What it means
flushOrRefuse is the pre-flight gate before opening a session from the page drawer: it refuses to save if any editor on the page currently holds text that is not valid JSON. Throwing before flushing guarantees no stale or broken buffer is written and the session doesn't open on lost changes.
Source
Thrown at frontend/src/lib/components/sessions/pageDrawerSession.ts:50
Object.entries(TRIGGER_PAGES).map(([kind, p]) => [
p.path,
`trigger_${kind as TriggerKind}` as UserDraftItemKind
])
)
}
// 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) =>View on GitHub (pinned to e474e8803c)
Solutions
- Fix the JSON in the flagged editor until it parses, then retry
- Discard the invalid editor changes before opening the session
- Use the editor's format/lint indicator to locate the syntax error
Example fix
// before
{ "a": 1, }
// after
{ "a": 1 } Defensive patterns
Strategy: validation
Validate before calling
if (anyEditorUnparseable()) return; // don't attempt to open a session await openSession(query)
Type guard
function isParseableJson(s: string): boolean { try { JSON.parse(s); return true } catch { return false } } Try / catch
try { await openSession(query) } catch (e) { if (/not valid JSON/.test(String(e))) focusFirstUnparseableEditor(); else throw e } Prevention
- Validate editor content on every keystroke and disable 'open session' while invalid
- Run JSON.parse before staging any editor text programmatically
- Show inline syntax errors so users fix them before saving
When it happens
Trigger: Calling pageDrawerSession's open/flush path (via pageDrawerSessionSource) while anyEditorUnparseable() reports an editor with invalid JSON in its buffer.
Common situations: User typed a partial JSON edit (trailing comma, unclosed brace) in a resource/script arg editor and immediately clicked 'open session'; programmatically set editor text that wasn't validated.
Related errors
- Completed jobs file must contain an array of jobs
- Queued jobs file must contain an array of jobs
- Invalid JSON for ${field}: ${errorMessage}
- This draft has a newer conflicting version on the server. Re
- Saving the latest draft failed (${failureMessage ?? 'unknown
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/4892c475a4ddd9ba.
Report an issue: GitHub.