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
- Inspect sdkTask.metadata in a debugger/log to confirm whether '__persistedState' is present and well-formed before calling reconstruct.
- 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.
- 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).
- 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
- Always populate task.metadata.__persistedState (with _agentSettings and _taskState) before calling taskStore.save.
- Add an integration test that round-trips a task through save then reconstruct to catch schema drift.
- Version the persisted-state schema and migrate old tasks on boot.
- If using a custom TaskStore, add a contract test that metadata keys survive serialization.
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
- Task ${taskId} is missing persisted state in metadata.
- Loaded metadata for task ${taskId} is missing internal persi
- GCS bucket name is required.
- Failed to create GCS bucket ${this.bucketName}: ${createErro
- Failed to initialize GCS bucket ${this.bucketName}: ${error}
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/74a39d92f4575c0a.
Report an issue: GitHub.