sveltejs/svelte · error · Error

fork_discarded

fork_discarded

Error message

fork_discarded
Cannot commit a fork that was already discarded
https://svelte.dev/e/fork_discarded

What it means

Thrown at reactivity/batch.js:1401 inside a fork's commit() when batch.linked is false, meaning the fork has already been discarded. A fork created via the experimental fork() API holds speculative state; once discard() runs (e.g. because navigation moved elsewhere) the fork is unlinked from the main state graph and committing it would lose data integrity.

Solutions

  1. Track fork lifecycle explicitly and guard commit() behind an `if (!discarded)` flag you own.
  2. Call discard() only after you are certain no commit will follow, or cancel in-flight commits first.
  3. Use try/catch around commit() to tolerate races and log/handle the stale fork.

Example fix

// before
const f = fork(() => { ... });
f.discard();
await f.commit(); // throws

// after
const f = fork(() => { ... });
let active = true;
// on navigation: active = false; f.discard();
async function maybeCommit() {
  if (!active) return;
  try { await f.commit(); }
  catch (e) { /* fork already discarded */ }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Track fork lifecycle explicitly before committing.
let forkActive = true;
const f = fork(() => { ... });
function discardFork() { forkActive = false; f.discard(); }
async function commitFork() {
  if (!forkActive) return;
  try { await f.commit(); }
  catch (e) { if (!forkActive) return; throw e; }
}

Try / catch

try {
  await f.commit();
} catch (e) {
  if (e?.message?.includes('fork_discarded') || e?.message?.includes('svelte.dev/e/fork_discarded')) {
    // fork already discarded — ignore
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling fork().commit() after fork().discard() was already invoked, or after the fork was auto-discarded by a navigation/route change. The guard at batch.js:1400-1402 checks batch.linked.

Common situations: Optimistic-update forks in routing where the user navigates away then a stale promise resolves and tries to commit; holding a fork reference in a long-lived cache and committing after cleanup; race between discard and an in-flight async commit.

Related errors


AI-assisted analysis of sveltejs/svelte@20b341f100 (2026-08-12). Data as JSON: /api/errors/16f24c7538e85a33. Report an issue: GitHub.

Appendix: source

Thrown at packages/svelte/src/internal/client/errors.js:279

		throw error;
	} else {
		throw new Error(`https://svelte.dev/e/flush_sync_in_effect`);
	}
}

/**
 * Cannot commit a fork that was already discarded
 * @returns {never}
 */
export function fork_discarded() {
	if (DEV) {
		const error = new Error(`fork_discarded\nCannot commit a fork that was already discarded\nhttps://svelte.dev/e/fork_discarded`);

		error.name = 'Svelte error';

		throw error;
	} else {
		throw new Error(`https://svelte.dev/e/fork_discarded`);
	}
}

/**
 * Cannot create a fork inside an effect or when state changes are pending
 * @returns {never}
 */
export function fork_timing() {
	if (DEV) {
		const error = new Error(`fork_timing\nCannot create a fork inside an effect or when state changes are pending\nhttps://svelte.dev/e/fork_timing`);

		error.name = 'Svelte error';

		throw error;
	} else {
		throw new Error(`https://svelte.dev/e/fork_timing`);
	}
}

View on GitHub (pinned to 20b341f100)