stablyai/orca · error

Invalid external automation job ID.

Error message

Invalid external automation job ID.

What it means

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.

Source

Thrown at src/main/automations/external-manager.ts:326

        .getSshTargets()
        // Why: runtime-owned hidden targets are excluded from SSH/run-target
        // surfaces; don't probe them for external automations either.
        .filter((target) => !isRuntimeOwnedSshTarget(target))
        .flatMap((target) => [listRemoteHermesManager(target), listRemoteOpenClawManager(target)])
    )
  ])
  return [
    ...(localHermes ? [localHermes] : []),
    ...(localOpenClaw ? [localOpenClaw] : []),
    ...remote
  ]
}

export async function listExternalAutomationRuns(
  input: ExternalAutomationRunsInput
): Promise<ExternalAutomationRunsPage> {
  if (!EXTERNAL_JOB_ID_PATTERN.test(input.jobId)) {
    throw new Error('Invalid external automation job ID.')
  }
  const page = Number.isFinite(input.page) ? Math.max(1, Math.floor(input.page)) : 1
  const pageSize = Number.isFinite(input.pageSize)
    ? Math.min(100, Math.max(1, Math.floor(input.pageSize)))
    : 25
  if (input.provider !== 'hermes') {
    return {
      managerId: input.managerId,
      provider: input.provider,
      target: input.target,
      jobId: input.jobId,
      page,
      pageSize,
      total: 0,
      runs: []
    }
  }
  if (input.target.type === 'local') {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Sanitize/validate the jobId with EXTERNAL_JOB_ID_PATTERN before calling listExternalAutomationRuns.
  2. Strip whitespace and any path/URL prefix to extract the bare ID.
  3. If the ID genuinely needs other characters, change the upstream producer to emit IDs matching the pattern.

Example fix

// before
listExternalAutomationRuns({ jobId: '  repo/job #1', ... }) // leading space + slash -> throws

// after
const jobId = (raw.trim().match(/^[A-Za-z0-9][A-Za-z0-9._:-]*/) ?? [])[0]
if (!jobId) throw new UserError('Invalid job ID')
listExternalAutomationRuns({ jobId, ... })
Defensive patterns

Strategy: validation

Validate before calling

const EXTERNAL_JOB_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/

function sanitizeJobId(raw: string): string | null {
  const trimmed = raw.trim()
  const match = trimmed.match(EXTERNAL_JOB_ID_PATTERN)
  return match ? match[0] : null
}

const jobId = sanitizeJobId(raw)
if (!jobId) throw new UserError('Job ID must start with a letter or digit and contain only A-Z a-z 0-9 . _ : -')
listExternalAutomationRuns({ jobId, ... })

Type guard

function isExternalJobId(v: unknown): v is string {
  return typeof v === 'string' && /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(v)
}

Try / catch

try {
  await listExternalAutomationRuns(input)
} catch (e) {
  if ((e as Error).message === 'Invalid external automation job ID.') {
    throw new UserError('That job ID is not valid. Use only letters, digits, . _ : - and start with a letter or digit.')
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/5aa9b1a94dbd270c. Report an issue: GitHub.