{"record":{"id":"5aa9b1a94dbd270c","repo":"stablyai/orca","slug":"invalid-external-automation-job-id","errorCode":null,"errorMessage":"Invalid external automation job ID.","messagePattern":"Invalid external automation job ID\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/automations/external-manager.ts","lineNumber":326,"sourceCode":"        .getSshTargets()\n        // Why: runtime-owned hidden targets are excluded from SSH/run-target\n        // surfaces; don't probe them for external automations either.\n        .filter((target) => !isRuntimeOwnedSshTarget(target))\n        .flatMap((target) => [listRemoteHermesManager(target), listRemoteOpenClawManager(target)])\n    )\n  ])\n  return [\n    ...(localHermes ? [localHermes] : []),\n    ...(localOpenClaw ? [localOpenClaw] : []),\n    ...remote\n  ]\n}\n\nexport async function listExternalAutomationRuns(\n  input: ExternalAutomationRunsInput\n): Promise<ExternalAutomationRunsPage> {\n  if (!EXTERNAL_JOB_ID_PATTERN.test(input.jobId)) {\n    throw new Error('Invalid external automation job ID.')\n  }\n  const page = Number.isFinite(input.page) ? Math.max(1, Math.floor(input.page)) : 1\n  const pageSize = Number.isFinite(input.pageSize)\n    ? Math.min(100, Math.max(1, Math.floor(input.pageSize)))\n    : 25\n  if (input.provider !== 'hermes') {\n    return {\n      managerId: input.managerId,\n      provider: input.provider,\n      target: input.target,\n      jobId: input.jobId,\n      page,\n      pageSize,\n      total: 0,\n      runs: []\n    }\n  }\n  if (input.target.type === 'local') {","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/automations/external-manager.ts#L308-L344","documentation":"Thrown by listExternalAutomationRuns() when input.jobId fails EXTERNAL_JOB_ID_PATTERN (`/^[A-Za-z0-9][A-Za-z0-9._:-]*$/`). The job ID must start alphanumeric and contain only alphanumerics, dot, underscore, colon, or hyphen. This validates the ID before it is sent to a local Hermes process or relayed over an SSH multiplexer, rejecting malformed/illegal input early.","triggerScenarios":"Passing a jobId with a leading symbol, whitespace, slashes, or any char outside the allowed set; passing an empty string; passing a URL or path-like value instead of a bare job ID.","commonSituations":"UI forwards a user-typed string with spaces or slashes; a script passes a full URL or `repo#job` token; an injection attempt containing shell metacharacters; copy-paste includes surrounding quotes.","solutions":["Sanitize/validate the jobId with EXTERNAL_JOB_ID_PATTERN before calling listExternalAutomationRuns.","Strip whitespace and any path/URL prefix to extract the bare ID.","If the ID genuinely needs other characters, change the upstream producer to emit IDs matching the pattern."],"exampleFix":"// before\nlistExternalAutomationRuns({ jobId: '  repo/job #1', ... }) // leading space + slash -> throws\n\n// after\nconst jobId = (raw.trim().match(/^[A-Za-z0-9][A-Za-z0-9._:-]*/) ?? [])[0]\nif (!jobId) throw new UserError('Invalid job ID')\nlistExternalAutomationRuns({ jobId, ... })","handlingStrategy":"validation","validationCode":"const EXTERNAL_JOB_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/\n\nfunction sanitizeJobId(raw: string): string | null {\n  const trimmed = raw.trim()\n  const match = trimmed.match(EXTERNAL_JOB_ID_PATTERN)\n  return match ? match[0] : null\n}\n\nconst jobId = sanitizeJobId(raw)\nif (!jobId) throw new UserError('Job ID must start with a letter or digit and contain only A-Z a-z 0-9 . _ : -')\nlistExternalAutomationRuns({ jobId, ... })","typeGuard":"function isExternalJobId(v: unknown): v is string {\n  return typeof v === 'string' && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(v)\n}","tryCatchPattern":"try {\n  await listExternalAutomationRuns(input)\n} catch (e) {\n  if ((e as Error).message === 'Invalid external automation job ID.') {\n    throw new UserError('That job ID is not valid. Use only letters, digits, . _ : - and start with a letter or digit.')\n  }\n  throw e\n}","preventionTips":["Validate job IDs at the UI input boundary with the same regex before forwarding to the manager.","Never pass URLs, paths, or `repo#id` tokens as jobId — extract the bare ID first.","Trim and reject empty strings explicitly so they do not reach the manager."],"tags":["automations","validation","hermes","input-sanitization"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}