nexu-io/open-design · error · Error

Vela ${command} returned invalid JSON: ${detail}

Error message

Vela ${command} returned invalid JSON: ${detail}

What it means

Thrown by parseJsonObject() when stdout is non-empty but JSON.parse fails or the parsed value is not a JSON object (e.g. it is an array or primitive). The detail from the parse error or the 'expected a JSON object' message is appended. This distinguishes malformed-JSON failures from empty-output failures (error 484).

Source

Thrown at apps/daemon/src/media/vela.ts:88

function isRecord(value: unknown): value is JsonRecord {
  return value !== null && typeof value === 'object' && !Array.isArray(value);
}

function parseJsonObject(stdout: string, command: string): JsonRecord {
  const trimmed = stdout.trim();
  if (!trimmed) {
    throw new Error(`Vela ${command} returned no JSON output`);
  }
  try {
    const value: unknown = JSON.parse(trimmed);
    if (!isRecord(value)) {
      throw new Error('expected a JSON object');
    }
    return value;
  } catch (error) {
    const detail = error instanceof Error ? error.message : String(error);
    throw new Error(`Vela ${command} returned invalid JSON: ${detail}`);
  }
}

function nonEmptyString(value: unknown): string | null {
  return typeof value === 'string' && value.trim() ? value.trim() : null;
}

function positiveIntegerFromEnv(name: string, fallback: number): number {
  const parsed = Number(process.env[name]);
  return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
}

function wireModelForVela(model: string, wireModel: string): string {
  // A configured OD alias is already the provider-facing wire name. Only
  // remove the catalogue namespace when the alias layer left the id intact.
  if (wireModel !== model) return wireModel;
  return model.startsWith('vela/') ? model.slice('vela/'.length) : model;
}

View on GitHub (pinned to 5be4028344)

Solutions

  1. Run the failing Vela command directly and capture stdout to see the non-JSON content
  2. Upgrade or pin the Vela CLI version to one that emits a clean JSON object for that subcommand
  3. Ensure no shell wrapper or RC file prints to stdout when the Vela binary runs
  4. If the CLI emits logs on stdout, redirect them to stderr in the command runner
Defensive patterns

Strategy: try-catch

Validate before calling

function looksLikeJsonObject(stdout: string): boolean {
  const t = stdout.trim();
  return t.startsWith('{') && t.endsWith('}');
}

const stdout = await runCommand(args, opts);
if (!looksLikeJsonObject(stdout)) {
  throw new Error(`Vela ${label} stdout is not a JSON object: ${stdout.slice(0, 120)}`);
}

Type guard

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

Try / catch

try {
  const value: unknown = JSON.parse(stdout);
  if (!isJsonRecord(value)) throw new Error('expected a JSON object');
} catch (err) {
  // log the raw stdout for schema diagnosis, then rethrow
  throw err;
}

Prevention

When it happens

Trigger: Vela CLI emits non-JSON text (a banner, log line, partial JSON, or an error message) on stdout, or emits valid JSON that is an array/scalar rather than an object.

Common situations: Vela CLI prints a human-readable error or progress line to stdout before/instead of JSON; CLI version change altered the output shape; a proxy or wrapper around the Vela binary injects text; the CLI emitted a JSON array for a command the daemon expects to return an object.

Understand the failure class

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/89420b3b8cfb28aa. Report an issue: GitHub.