google-gemini/gemini-cli · error · Error

Cannot reconstruct task ${sdkTask.id}: missing persisted sta

Error message

Cannot reconstruct task ${sdkTask.id}: missing persisted state in metadata.

What it means

Thrown by CoderAgentExecutor.reconstruct when rebuilding a TaskWrapper from a persisted SDKTask whose metadata lacks the '__persistedState' key. The persisted state must contain valid '_agentSettings' (with kind StateAgentSettingsEvent and a string workspacePath) and '_taskState' fields, validated by isPersistedStateMetadata. Without it, the runtime cannot restore the agent's prior configuration and execution state.

Source

Thrown at packages/a2a-server/src/agent/executor.ts:176

    const wrapper = this.tasks.get(taskId);
    if (wrapper) {
      wrapper.task.dispose();
      this.tasks.delete(taskId);
    }
  }

  /**
   * Reconstructs TaskWrapper from SDKTask.
   */
  async reconstruct(
    sdkTask: SDKTask,
    eventBus?: ExecutionEventBus,
  ): Promise<TaskWrapper> {
    const metadata = sdkTask.metadata || {};
    const persistedState = getPersistedState(metadata);

    if (!persistedState) {
      throw new Error(
        `Cannot reconstruct task ${sdkTask.id}: missing persisted state in metadata.`,
      );
    }

    let agentSettings: AgentSettings;
    try {
      agentSettings = {
        ...(persistedState._agentSettings ?? {}),
        workspacePath: validateWorkspacePath(
          persistedState._agentSettings?.workspacePath,
        ),
        isTrusted: false,
      };
    } catch (error) {
      logger.error(
        `[CoderAgentExecutor] Invalid workspace path in persisted state for task ${sdkTask.id}:`,
        error,
      );

View on GitHub (pinned to 5024443c72)

Solutions

  1. Inspect sdkTask.metadata in a debugger/log to confirm whether '__persistedState' is present and well-formed before calling reconstruct.
  2. If the task predates the persisted-state schema, treat it as unrecoverable: drop it from the store and create a fresh task instead of reconstructing.
  3. Ensure every code path that calls taskStore.save(task) does so AFTER the task's metadata has been populated with _agentSettings and _taskState (see executor.ts save sites around newTaskSDK).
  4. If using a custom TaskStore, verify it round-trips metadata verbatim without JSON key loss or size truncation.

Example fix

// before
const wrapper = await agentExecutor.reconstruct(sdkTask, eventBus);

// after
import { getPersistedState } from '../types.js';
if (!getPersistedState(sdkTask.metadata || {})) {
  logger.warn(`Task ${sdkTask.id} has no persisted state; creating fresh.`);
  wrapper = await agentExecutor.createTask(sdkTask.id, sdkTask.contextId, defaultAgentSettings, eventBus);
} else {
  wrapper = await agentExecutor.reconstruct(sdkTask, eventBus);
}
Defensive patterns

Strategy: validation

Validate before calling

import { getPersistedState } from '../types.js';

function canReconstruct(sdkTask: { metadata?: unknown }): boolean {
  return getPersistedState((sdkTask.metadata || {}) as Record<string, unknown>) !== undefined;
}

// before calling reconstruct:
if (!canReconstruct(sdkTask)) {
  logger.warn(`Task ${sdkTask.id} not reconstructable; creating fresh.`);
  return agentExecutor.createTask(sdkTask.id, sdkTask.contextId, defaultSettings, eventBus);
}

Type guard

import { METADATA_KEY } from '../types.js';

function hasPersistedState(t: { metadata?: unknown }): t is { metadata: { __persistedState: { _agentSettings: unknown; _taskState: unknown } } } {
  const m = t.metadata as Record<string, unknown> | undefined;
  const s = m?.[METADATA_KEY];
  return typeof s === 'object' && s !== null && '_agentSettings' in s && '_taskState' in s;
}

Try / catch

try {
  return await agentExecutor.reconstruct(sdkTask, eventBus);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Cannot reconstruct task')) {
    // unrecoverable persisted state - fall back to fresh task or fail the task cleanly
    return agentExecutor.createTask(sdkTask.id, sdkTask.contextId, defaultSettings, eventBus);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling agentExecutor.reconstruct(sdkTask) where sdkTask.metadata has no '__persistedState' entry, or the entry fails the isPersistedStateMetadata shape check (missing _agentSettings/_taskState, or _agentSettings.workspacePath not a string). Happens on task resume after server restart when the TaskStore returns a task whose metadata was never populated or was partially saved.

Common situations: Resuming a task whose save failed mid-flight (network error during taskStore.save); upgrading the server to a version that changed the persisted state schema while old tasks remain in the store; a custom TaskStore implementation that strips or renames metadata keys; load balancer routing a request for a task persisted by an older build.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/74a39d92f4575c0a. Report an issue: GitHub.