windmill-labs/windmill · error

state.failureMessage ?? `Failed to delete draft "${path}".`

Error message

state.failureMessage ?? `Failed to delete draft "${path}".`

What it means

deleteGlobalDraft() performs the delete through UserDraftDbSyncer, which records failures (network errors, 5xx responses) in its state map instead of throwing synchronously. After the delete call, the adapter checks getState() and re-throws this error (preferring the syncer's own failureMessage) so callers never conclude a draft was removed when the DB-backed source of truth still contains it. The generic message is the fallback when the syncer stored no failureMessage.

Source

Thrown at frontend/src/lib/components/copilot/chat/global/userDraftAdapter.ts:609

		UserDraft.remove(itemKind, storagePath, { workspace })
	} else {
		UserDraft.clear(itemKind, storagePath, { workspace })
	}
	// `remove`/`clear` only debounce the delete; persist it now so a deploy/discard
	// that the caller awaits has actually cleared the server draft on return.
	await UserDraftDbSyncer.save({
		workspace,
		itemKind,
		path: storagePath,
		value: null,
		immediate: true
	})
	// A failed (network/5xx) or conflicted delete is recorded in the syncer state,
	// not thrown — surface it so callers don't report the draft as removed while
	// the DB-backed source of truth still has it (same guard as the write path).
	const state = UserDraftDbSyncer.getState({ workspace, itemKind, path: storagePath })
	if (state.state === 'failed') {
		throw new Error(state.failureMessage ?? `Failed to delete draft "${path}".`)
	}
	if (UserDraftDbSyncer.getConflict({ workspace, itemKind, path: storagePath }).conflict) {
		throw new Error(
			`Draft "${path}" changed externally since you last read it; it was not removed. Re-read and retry.`
		)
	}
	invalidateWorkspaceDrafts(workspace)
}

/** Kind-addressed live-editor storage resolution (friendly → storage path),
 * for callers that must probe several draft kinds per chat type. */
export function resolveGlobalDraftStoragePathByKind(
	workspace: string,
	itemKind: UserDraftItemKind,
	path: string
): string {
	return resolveDraftStoragePath(workspace, itemKind, path)
}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Read state.failureMessage via UserDraftDbSyncer.getState() to see the underlying cause before retrying.
  2. Check backend connectivity/auth, then retry deleteGlobalDraft.
  3. Inspect the network tab for the failing DELETE request and its status code.
  4. If failureMessage is empty, improve the syncer to always populate it so users get actionable messages.

Example fix

// before
try { await deleteGlobalDraft(ws, 'script', p) } catch { /* assumed removed */ }
// after
try {
  await deleteGlobalDraft(ws, 'script', p)
} catch (e) {
  const st = UserDraftDbSyncer.getState({ workspace: ws, itemKind: 'script', path: p })
  notify(st.failureMessage ?? String(e))
}
Defensive patterns

Strategy: try-catch

Validate before calling

const st = UserDraftDbSyncer.getState({ workspace, itemKind, path })
if (st.state === 'failed') {
  throw new Error(st.failureMessage ?? 'Previous sync failed; check connectivity before deleting')
}
if (!navigator.onLine) throw new Error('Offline: draft deletion is unavailable')

Try / catch

try {
  await deleteGlobalDraft(workspace, itemKind, path)
} catch (e) {
  const st = UserDraftDbSyncer.getState({ workspace, itemKind, path })
  notify(st.failureMessage ?? (e instanceof Error ? e.message : 'Delete failed'))
  // do NOT treat the draft as removed
}

Prevention

When it happens

Trigger: UserDraftDbSyncer.delete leaves state 'failed' — typically the underlying HTTP DELETE to the backend failed (offline, server 5xx, timeout) or the syncer rejected the request; state.failureMessage is undefined so the generic fallback message is thrown.

Common situations: Deleting a draft while the backend is unreachable or restarting; a proxy returning 5xx; expired session/auth causing the API call to fail; calling deleteGlobalDraft from chat tools during a network outage.

Related errors


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