google-gemini/gemini-cli · error · Error
Invalid taskId: ${taskId}
Error message
Invalid taskId: ${taskId} What it means
Thrown by GCSTaskStore.getObjectPath when isTaskIdValid(taskId) fails - the regex /^[a-zA-Z0-9_-]+$/ requires non-empty alphanumeric with only hyphens/underscores. This is a path-traversal guard: taskId is interpolated directly into the GCS object name `tasks/<id>/<type>.tar.gz`, so slashes, dots, or special chars could escape the tasks/ prefix. getObjectPath runs on every save/load.
Source
Thrown at packages/a2a-server/src/persistence/gcs.ts:89
logger.info(`Bucket ${this.bucketName} exists.`);
}
} catch (error) {
logger.info(
`Error during bucket initialization for ${this.bucketName}: ${error}`,
);
throw new Error(
`Failed to initialize GCS bucket ${this.bucketName}: ${error}`,
);
}
}
private async ensureBucketInitialized(): Promise<void> {
await this.bucketInitialized;
}
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');View on GitHub (pinned to 5024443c72)
Solutions
- Generate taskIds with a known-safe format: UUIDv4 (hyphens OK) or nanoid with the default alphabet.
- Sanitize incoming taskIds at the API boundary: strip/replace characters outside [A-Za-z0-9_-] before they reach the store.
- Reject the request at the handler with a 400 rather than letting the store throw.
- Add a unit test asserting your ID generator's output always matches /^[a-zA-Z0-9_-]+$/.
Example fix
// before
const id = `${userScope}:${crypto.randomUUID()}`;
store.save({ id, ... });
// throws: Invalid taskId (contains ':')
// after
const id = `${userScope}_${crypto.randomUUID().replaceAll('-', '_')}`;
// or validate at the boundary:
if (!/^[a-zA-Z0-9_-]+$/.test(id)) throw new TypeError('Invalid taskId');
store.save({ id, ... }); Defensive patterns
Strategy: validation
Validate before calling
const TASK_ID_RE = /^[a-zA-Z0-9_-]+$/;
function assertTaskIdValid(id: string) {
if (!TASK_ID_RE.test(id)) throw new TypeError(`Invalid taskId '${id}'; expected ${TASK_ID_RE}`);
}
// call at the API boundary, before store.save/load Type guard
function isValidTaskId(id: string): id is string {
return /^[a-zA-Z0-9_-]+$/.test(id);
} Try / catch
try {
await store.save(task);
} catch (e) {
if (e instanceof Error && e.message.startsWith('Invalid taskId')) {
return { status: 400, error: e.message };
}
throw e;
} Prevention
- Generate taskIds with UUIDv4 or nanoid - their default alphabets satisfy the regex.
- Validate at the API boundary and return 400, never let untrusted IDs reach the store.
- Strip/replace ':' or '/' from any upstream ID scheme before composing taskIds.
- Add a property test asserting your ID generator's output matches /^[a-zA-Z0-9_-]+$/.
When it happens
Trigger: save(task) or load(taskId) is called with a taskId containing '/', '..', '.', spaces, or any non-[A-Za-z0-9_-] character. The regex rejects it before composing the object key, preventing traversal like `../../other-task/workspace`.
Common situations: Client supplies a taskId with a URL path segment or UUID with dots; SDK generates IDs that include ':' (common in some UUID variants); accidental concatenation like 'user:abc-123'; malicious input probing for traversal.
Related errors
- Security violation: Null byte detected in path.
- Security violation: The path "${trimmedPath}" is outside the
- Invalid path: Directory traversal not allowed.
- Invalid skill name: Path traversal detected.
- Path validation failed: ${pathError}
AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12).
Data as JSON: /api/errors/4aecd8b91c8c84e1.
Report an issue: GitHub.