stablyai/orca · error · Error

file does not contain a JSON object

Error message

file does not contain a JSON object

What it means

Thrown inside readPersistedState (then re-wrapped as error 539) when the persisted agent-hooks state file parses as valid JSON but the top-level value is not a plain object — e.g. it is an array, a number, a string, or null. isRecord() rejects arrays and null explicitly, so only a real key-value object passes. This inner Error is caught and converted to a RuntimeClientError with the path context.

Source

Thrown at src/cli/handlers/agent-hooks.ts:43

  statuses: AgentHookInstallStatus[]
}

function getDataPath(): string {
  return join(getDefaultUserDataPath(), 'orca-data.json')
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

function readPersistedState(dataPath: string): PersistedState {
  if (!existsSync(dataPath)) {
    return getDefaultPersistedState(homedir())
  }
  try {
    const parsed = JSON.parse(readFileSync(dataPath, 'utf-8'))
    if (!isRecord(parsed)) {
      throw new Error('file does not contain a JSON object')
    }
    return parsed as PersistedState
  } catch (error) {
    throw new RuntimeClientError(
      'runtime_error',
      `Could not read ${dataPath}: ${error instanceof Error ? error.message : String(error)}`
    )
  }
}

function writePersistedState(dataPath: string, state: PersistedState): void {
  mkdirSync(dirname(dataPath), { recursive: true })
  const tmpPath = join(dirname(dataPath), `.${Date.now()}-${randomUUID()}.tmp`)
  let renamed = false
  try {
    writeFileSync(tmpPath, `${JSON.stringify(state, null, 2)}\n`, 'utf-8')
    renameSync(tmpPath, dataPath)
    renamed = true

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Restore the file to a JSON object shape (or delete it to regenerate defaults via getDefaultPersistedState).
  2. Back up the current file, then `rm <dataPath>` so the next run recreates a valid default state.
  3. Validate with `jq empty <file>` and ensure `jq -e 'type=="object"' <file>` succeeds.

Example fix

// before: ~/.config/orca/agent-hooks.json contains `[]`

// after: replace with an object, or remove the file
rm ~/.config/orca/agent-hooks.json
Defensive patterns

Strategy: type-guard

Validate before calling

import { readFileSync, existsSync } from 'node:fs'

function readStateObject(dataPath: string): Record<string, unknown> {
  if (!existsSync(dataPath)) return {}
  const parsed = JSON.parse(readFileSync(dataPath, 'utf-8'))
  if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
    throw new Error(`${dataPath} root is not a JSON object`)
  }
  return parsed as Record<string, unknown>
}

Type guard

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value)
}

Prevention

When it happens

Trigger: The dataPath file contains JSON.parse-able content whose root is an array, primitive, or null. Examples: '[]', '42', '"hello"', 'null'. Note malformed JSON throws a SyntaxError instead (still caught at 539).

Common situations: User hand-edited the agent-hooks state file and saved an array, an external tool wrote a non-object schema, or a prior version wrote a different top-level shape.

Related errors


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