{"record":{"id":"74a39d92f4575c0a","repo":"google-gemini/gemini-cli","slug":"cannot-reconstruct-task-sdktask-id-missing-per","errorCode":null,"errorMessage":"Cannot reconstruct task ${sdkTask.id}: missing persisted state in metadata.","messagePattern":"Cannot reconstruct task (.+?): missing persisted state in metadata\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/a2a-server/src/agent/executor.ts","lineNumber":176,"sourceCode":"    const wrapper = this.tasks.get(taskId);\n    if (wrapper) {\n      wrapper.task.dispose();\n      this.tasks.delete(taskId);\n    }\n  }\n\n  /**\n   * Reconstructs TaskWrapper from SDKTask.\n   */\n  async reconstruct(\n    sdkTask: SDKTask,\n    eventBus?: ExecutionEventBus,\n  ): Promise<TaskWrapper> {\n    const metadata = sdkTask.metadata || {};\n    const persistedState = getPersistedState(metadata);\n\n    if (!persistedState) {\n      throw new Error(\n        `Cannot reconstruct task ${sdkTask.id}: missing persisted state in metadata.`,\n      );\n    }\n\n    let agentSettings: AgentSettings;\n    try {\n      agentSettings = {\n        ...(persistedState._agentSettings ?? {}),\n        workspacePath: validateWorkspacePath(\n          persistedState._agentSettings?.workspacePath,\n        ),\n        isTrusted: false,\n      };\n    } catch (error) {\n      logger.error(\n        `[CoderAgentExecutor] Invalid workspace path in persisted state for task ${sdkTask.id}:`,\n        error,\n      );","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/a2a-server/src/agent/executor.ts#L158-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst wrapper = await agentExecutor.reconstruct(sdkTask, eventBus);\n\n// after\nimport { getPersistedState } from '../types.js';\nif (!getPersistedState(sdkTask.metadata || {})) {\n  logger.warn(`Task ${sdkTask.id} has no persisted state; creating fresh.`);\n  wrapper = await agentExecutor.createTask(sdkTask.id, sdkTask.contextId, defaultAgentSettings, eventBus);\n} else {\n  wrapper = await agentExecutor.reconstruct(sdkTask, eventBus);\n}","handlingStrategy":"validation","validationCode":"import { getPersistedState } from '../types.js';\n\nfunction canReconstruct(sdkTask: { metadata?: unknown }): boolean {\n  return getPersistedState((sdkTask.metadata || {}) as Record<string, unknown>) !== undefined;\n}\n\n// before calling reconstruct:\nif (!canReconstruct(sdkTask)) {\n  logger.warn(`Task ${sdkTask.id} not reconstructable; creating fresh.`);\n  return agentExecutor.createTask(sdkTask.id, sdkTask.contextId, defaultSettings, eventBus);\n}","typeGuard":"import { METADATA_KEY } from '../types.js';\n\nfunction hasPersistedState(t: { metadata?: unknown }): t is { metadata: { __persistedState: { _agentSettings: unknown; _taskState: unknown } } } {\n  const m = t.metadata as Record<string, unknown> | undefined;\n  const s = m?.[METADATA_KEY];\n  return typeof s === 'object' && s !== null && '_agentSettings' in s && '_taskState' in s;\n}","tryCatchPattern":"try {\n  return await agentExecutor.reconstruct(sdkTask, eventBus);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Cannot reconstruct task')) {\n    // unrecoverable persisted state - fall back to fresh task or fail the task cleanly\n    return agentExecutor.createTask(sdkTask.id, sdkTask.contextId, defaultSettings, eventBus);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["persistence","task-state","reconstruction","metadata"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}