google-gemini/gemini-cli · error · Error

Task ${taskId} is missing persisted state in metadata.

Error message

Task ${taskId} is missing persisted state in metadata.

What it means

Thrown by GCSTaskStore.save() when the task being persisted has no valid __persistedState entry in its metadata. The persisted state (stored under the METADATA_KEY '__persistedState') must contain an _agentSettings object (with kind === 'state_agent_settings' and a string workspacePath) and a _taskState field. Without it, the GCS store has nothing durable to write.

Source

Thrown at packages/a2a-server/src/persistence/gcs.ts:103

  }

  private getObjectPath(taskId: string, type: ObjectType): string {
    if (!isTaskIdValid(taskId)) {
      throw new Error(`Invalid taskId: ${taskId}`);
    }
    return `tasks/${taskId}/${type}.tar.gz`;
  }

  async save(task: SDKTask): Promise<void> {
    await this.ensureBucketInitialized();
    const taskId = task.id;
    const persistedState = getPersistedState(
      // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
      task.metadata as PersistedTaskMetadata,
    );

    if (!persistedState) {
      throw new Error(`Task ${taskId} is missing persisted state in metadata.`);
    }
    const workDir = process.cwd();

    const metadataObjectPath = this.getObjectPath(taskId, 'metadata');
    const workspaceObjectPath = this.getObjectPath(taskId, 'workspace');

    const dataToStore = task.metadata;

    try {
      const jsonString = JSON.stringify(dataToStore);
      const compressedMetadata = gzipSync(Buffer.from(jsonString));
      const metadataFile = this.storage
        .bucket(this.bucketName)
        .file(metadataObjectPath);
      await metadataFile.save(compressedMetadata, {
        contentType: 'application/gzip',
      });
      logger.info(

View on GitHub (pinned to 5024443c72)

Solutions

  1. Ensure the task metadata is populated via setPersistedState(metadata, { _agentSettings, _taskState }) before calling save.
  2. Before calling save, verify getPersistedState(task.metadata) returns a non-undefined value.
  3. Confirm _agentSettings has kind === 'state_agent_settings' and a string workspacePath, and _taskState is present.

Example fix

// before
const task: SDKTask = { id, metadata: {}, ... };
await store.save(task); // throws

// after
import { setPersistedState } from '../types.js';
const task: SDKTask = {
  id,
  metadata: setPersistedState({}, {
    _agentSettings: { kind: 'state_agent_settings', workspacePath: '/work', ... },
    _taskState: 'submitted',
  }),
  ...,
};
await store.save(task);
Defensive patterns

Strategy: validation

Validate before calling

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

function canSaveTask(task: SDKTask): boolean {
  return getPersistedState(task.metadata as PersistedTaskMetadata) !== undefined;
}

// Before save:
if (!canSaveTask(task)) {
  task.metadata = setPersistedState(task.metadata, {
    _agentSettings: agentSettings,
    _taskState: currentState,
  });
}
await store.save(task);

Type guard

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

function hasValidPersistedState(metadata: unknown): boolean {
  return (
    typeof metadata === 'object' &&
    metadata !== null &&
    METADATA_KEY in metadata &&
    isPersistedStateMetadata((metadata as Record<string, unknown>)[METADATA_KEY])
  );
}

Try / catch

try {
  await store.save(task);
} catch (e) {
  if (e instanceof Error && e.message.includes('missing persisted state')) {
    // Repopulate and retry, or log and skip
    task.metadata = setPersistedState(task.metadata, state);
    await store.save(task);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling store.save(task) where task.metadata lacks the '__persistedState' key, or where the nested _agentSettings or _taskState are missing/malformed. This occurs when a task is constructed manually instead of through the agent executor that calls setPersistedState().

Common situations: Manually building an SDK Task object without populating persisted state; migrating from an older metadata format that didn't use __persistedState; a bug in the executor (executor.ts) that skips the setPersistedState() call before delegating to save.

Related errors


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