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.

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)

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.