tinyhumansai/openhuman · error · Error

Failed to parse service CLI output as JSON: parsed value doe

Error message

Failed to parse service CLI output as JSON: parsed value does not match CommandResponse shape

What it means

The sibling of the JSON.parse failure in parseServiceCliOutput(): stdout parsed as valid JSON, but isCommandResponse() — the structural check for the CommandResponse envelope, including that `logs` is an array of strings — rejected it. The CLI spoke JSON, just not the envelope this wrapper expects; typically a version mismatch between the CLI binary and the frontend types, or a bare value emitted where the `{ success, data, logs }` envelope was assumed.

Source

Thrown at app/src/utils/tauriCommands/common.ts:97

    return false;
  }
  if (!Array.isArray(candidate.logs)) {
    return false;
  }
  return candidate.logs.every(entry => typeof entry === 'string');
}

export function parseServiceCliOutput<T>(raw: string): CommandResponse<T> {
  let parsed: unknown;
  try {
    parsed = JSON.parse(raw);
  } catch (err) {
    throw new Error(
      `Failed to parse service CLI output as JSON: ${err instanceof Error ? err.message : String(err)}`
    );
  }
  if (!isCommandResponse<T>(parsed)) {
    throw new Error(
      'Failed to parse service CLI output as JSON: parsed value does not match CommandResponse shape'
    );
  }
  return parsed;
}

/**
 * Typed marker for the CEF "IPC bridge not wired" failure mode. The vendored
 * `app/src-tauri/vendor/tauri-cef/crates/tauri/scripts/ipc-protocol.js` falls
 * back to `window.ipc.postMessage(...)` whenever the custom-protocol fetch
 * rejects (network blip, navigation interrupt, mid-session re-entry). On CEF
 * `window.ipc` is never wired — `app/src-tauri/src/cef_impl.rs` drops the
 * `ipc_handler` registration — so the fallback throws
 * `TypeError: Cannot read properties of undefined (reading 'postMessage')`
 * **synchronously**, before the underlying `invoke()` constructs its Promise.
 * The throw escapes the Promise executor and lands on `onunhandledrejection`,
 * which Sentry then captures as `TAURI-REACT-7` / `TAURI-REACT-6` with no user
 * impact recorded because the call sites never caught it.

View on GitHub (pinned to a221052e0d)

Solutions

  1. Log the parsed value and diff it against isCommandResponse's requirements (notably `logs` must exist and be an array of strings).
  2. Rebuild/reinstall the matching service CLI binary so the envelope shape matches the frontend wrapper.
  3. If the contract legitimately changed, update isCommandResponse and the CommandResponse type together in the same commit.
  4. Make the CLI always wrap bare values into the envelope instead of emitting them raw.

Example fix

// before
const res = parseServiceCliOutput<T>(raw);
// after
const parsed = JSON.parse(raw) as unknown;
const res = isCommandResponse<T>(parsed)
  ? parsed
  : ({ success: true, data: parsed, logs: [] } as CommandResponse<T>); // tolerate bare values
Defensive patterns

Strategy: type-guard

Validate before calling

const parsed: unknown = JSON.parse(raw);
if (!isCommandResponse(parsed)) {
  // version drift or bare value — decide explicitly instead of letting parse throw
}

Type guard

function isCommandResponse<T>(v: unknown): v is CommandResponse<T> {
  if (typeof v !== 'object' || v === null) return false;
  const c = v as Record<string, unknown>;
  return (
    typeof c.success === 'boolean' &&
    Array.isArray(c.logs) &&
    c.logs.every((l) => typeof l === 'string')
  );
}

Try / catch

try {
  const res = parseServiceCliOutput<MyResult>(raw);
} catch (err) {
  if (err instanceof Error && err.message.includes('does not match CommandResponse shape')) {
    // log parsed shape + raw, then align CLI version or update the guard
  }
  throw err;
}

Prevention

When it happens

Trigger: The CLI outputs a bare result object without the envelope; the envelope schema changed across versions (renamed/missing `logs`, non-string log entries); an error object was printed as JSON on the success path; a test fixture omits `logs`.

Common situations: Frontend updated ahead of the bundled core binary (or vice versa) after an envelope change; a new CLI version serializes RpcOutcome without logs; hand-written test fixtures that never matched the real shape.

Understand the failure class

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/2198dbfe59d1ff4c. Report an issue: GitHub.