{"record":{"id":"38a165c5886de532","repo":"Yeachan-Heo/oh-my-codex","slug":"fieldname-contains-invalid-task-id-item","errorCode":null,"errorMessage":"${fieldName} contains invalid task ID: \"${item}\"","messagePattern":"(.+?) contains invalid task ID: \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/team/api-interop.ts","lineNumber":453,"sourceCode":"      snapshot_available: snapshot !== null,\n      phase_available: phaseState !== null,\n      recent_event_count: recentEvents.length,\n    },\n  };\n}\n\nfunction parseValidatedTaskIdArray(value: unknown, fieldName: string): string[] {\n  if (!Array.isArray(value)) {\n    throw new Error(`${fieldName} must be an array of task IDs (strings)`);\n  }\n  const taskIds: string[] = [];\n  for (const item of value) {\n    if (typeof item !== 'string') {\n      throw new Error(`${fieldName} entries must be strings`);\n    }\n    const normalized = item.trim();\n    if (!TASK_ID_SAFE_PATTERN.test(normalized)) {\n      throw new Error(`${fieldName} contains invalid task ID: \"${item}\"`);\n    }\n    taskIds.push(normalized);\n  }\n  return taskIds;\n}\n\nfunction teamStateExists(teamName: string, candidateCwd: string): boolean {\n  if (!TEAM_NAME_SAFE_PATTERN.test(teamName)) return false;\n  const teamRoot = join(resolveCanonicalTeamStateRoot(candidateCwd), 'team', teamName);\n  return existsSync(join(teamRoot, 'config.json')) || existsSync(join(teamRoot, 'tasks')) || existsSync(teamRoot);\n}\n\nfunction readTeamStateRootFromManifest(path: string): string | null {\n  if (!existsSync(path)) return null;\n  try {\n    const parsed = JSON.parse(readFileSync(path, 'utf8')) as { team_state_root?: unknown };\n    return typeof parsed.team_state_root === 'string' && parsed.team_state_root.trim() !== ''\n      ? parsed.team_state_root.trim()","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/api-interop.ts#L435-L471","documentation":"Thrown when a task ID string inside an array fails TASK_ID_SAFE_PATTERN after trimming (digits-only positive integer, max 20 digits). The library rejects IDs containing non-digit characters to keep CLI interop shell-safe.","triggerScenarios":"Passing task IDs like 'abc', '#12', '12.5', '  ' (empty after trim), or IDs longer than 20 digits within a task ID array field.","commonSituations":"Copying IDs from an external tracker that includes prefixes (e.g. 'TASK-42'); trailing whitespace or unicode digits; using a UUID as a task ID.","solutions":["Strip non-digit prefixes/suffixes before passing IDs","Verify each ID matches /^\\d{1,20}$/ (non-zero-leading positive integers if required)","Re-fetch canonical task IDs from the team API instead of constructing them manually"],"exampleFix":"// before\napi('update-tasks', { task_ids: ['TASK-42'] });\n// after\napi('update-tasks', { task_ids: ['42'] });","handlingStrategy":"type-guard","validationCode":"const TASK_ID = /^\\d{1,20}$/;\nconst valid = ids.every((id) => TASK_ID.test(id.trim()));\nif (!valid) throw new RangeError('task ids must be 1-20 digit integers');","typeGuard":"const isSafeTaskId = (id: string): boolean => /^\\d{1,20}$/.test(id.trim());","tryCatchPattern":"catch (e) { if (e instanceof Error && e.message.includes('invalid task ID')) { /* log offending field, re-prompt */ } throw e; }","preventionTips":["Never forward prefixed IDs from external trackers without normalization","Unit-test ID normalization against the same regex"],"tags":["validation","task-id","team-api"],"backgroundTag":"invalid-identifier-format","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}