can1357/oh-my-pi · warning

LSP configuration was superseded during reload: ${config.com

Error message

LSP configuration was superseded during reload: ${config.command}

What it means

During LSP client creation, getConfigOrCreate checks whether the client configuration for this key was invalidated by a concurrent reload. If a reload superseded the config while this coroutine was waiting (past the lock check), creating a client from the stale config could launch a server with outdated settings, so the library throws instead.

Source

Thrown at packages/coding-agent/src/lsp/client.ts:963

	cwd: string,
	initTimeoutMs?: number,
	signal?: AbortSignal,
): Promise<LspClient> {
	const key = clientKey(config, cwd);
	// Check if client already exists
	const existingClient = clients.get(key);
	if (existingClient && !invalidatedClientKeys.has(key)) {
		existingClient.lastActivity = Date.now();
		return existingClient;
	}

	// Check if another coroutine is already creating this client
	const existingLock = clientLocks.get(key);
	if (existingLock) {
		return existingLock.promise;
	}
	if (invalidatedClientKeys.has(key)) {
		throw new Error(`LSP configuration was superseded during reload: ${config.command}`);
	}

	// Do not start a fresh identity until superseded processes are confirmed stopped.
	const reloadBarrier = clientReloadBarriers.get(path.resolve(cwd));
	if (reloadBarrier) {
		try {
			await untilAborted(signal, reloadBarrier);
		} catch (error) {
			throwIfAborted(signal);
			throw error;
		}
		const clientAfterReload = clients.get(key);
		if (clientAfterReload && !invalidatedClientKeys.has(key)) {
			clientAfterReload.lastActivity = Date.now();
			return clientAfterReload;
		}
		const lockAfterReload = clientLocks.get(key);
		if (lockAfterReload) return lockAfterReload.promise;

View on GitHub (pinned to 9690622007)

Solutions

  1. Retry the operation — the next call will create the client from the fresh config
  2. Ensure config reloads don't race with first LSP usage (await reload completion before tool calls)
  3. If it recurs, check for repeated config-watch triggers (e.g. a file being rewritten in a loop)

Example fix

// before: fire tool call immediately after editing config
await updateConfig(); await lspHover(file, pos);
// after: await reload settling, then retry on supersession
await updateConfig(); await lspReloadSettled();
try { await lspHover(file, pos); } catch (e) { if (String(e).includes('superseded')) await lspHover(file, pos); }
Defensive patterns

Strategy: retry

Validate before calling

// No public pre-check for internal invalidated keys; best effort is ensuring config is stable
if (configWatcher.pendingReload) await configWatcher.settled();

Try / catch

try {
  const client = await getConfigOrCreate(config, cwd);
} catch (err) {
  if (String(err.message).includes('superseded during reload')) {
    await Bun.sleep(50); // let reload finish, then retry with fresh config
    return getConfigOrCreate(config, cwd);
  }
  throw err;
}

Prevention

When it happens

Trigger: Two concurrent callers race: caller A starts client creation, a config reload invalidates the key (invalidatedClientKeys), then A resumes past the clientLocks check and finds its key invalidated — the throw fires at the pre-lock recheck.

Common situations: Editing LSP server config (command, args) in .omp settings while a first tool call triggers server startup; hot-reloading config during warmup; multiple files opened simultaneously right after a config change.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0db95c2addcfcda1. Report an issue: GitHub.