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
- Log the parsed value and diff it against isCommandResponse's requirements (notably `logs` must exist and be an array of strings).
- Rebuild/reinstall the matching service CLI binary so the envelope shape matches the frontend wrapper.
- If the contract legitimately changed, update isCommandResponse and the CommandResponse type together in the same commit.
- 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
- Ship CLI and frontend wrapper changes to the envelope in the same commit.
- Add fixtures for both the envelope and bare-value forms to parse tests.
- When the contract changes, update isCommandResponse and CommandResponse together.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Finish choosing how OpenHuman runs (tap Continue on the setu
- OpenHuman could not reach its remote (cloud) runtime. Check
- Failed to parse service CLI output as JSON: ${err instanceof
- batch_id must be a kebab-case slug
- base_repo must be "tinyhumansai/openhuman" (got "${spec.base
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/2198dbfe59d1ff4c.
Report an issue: GitHub.