tinyhumansai/openhuman · error · Error
Failed to parse service CLI output as JSON: ${err instanceof
Error message
Failed to parse service CLI output as JSON: ${err instanceof Error ? err.message : String(err)} What it means
parseServiceCliOutput() takes the raw stdout of a service CLI invocation and JSON.parses it, expecting a CommandResponse envelope. This variant is thrown when JSON.parse itself fails — the bytes on stdout are not JSON at all: human-readable error text, panic messages, log lines, shell diagnostics, or an empty string.
Source
Thrown at app/src/utils/tauriCommands/common.ts:92
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return false;
}
const candidate = value as { result?: unknown; logs?: unknown };
if (!('result' in candidate) || !('logs' in candidate)) {
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 throwsView on GitHub (pinned to a221052e0d)
Solutions
- Capture and log the raw string when parsing fails so you can see exactly what the CLI printed.
- Verify the service CLI binary exists, is executable, and its version matches the frontend wrapper's contract.
- Route logs to stderr or a file so stdout stays pure JSON.
- Read stdout to EOF and, if the CLI emits trailing JSON documents, parse the last complete one instead of the whole stream.
Example fix
// before
const res = parseServiceCliOutput(raw);
// after
let res;
try {
res = parseServiceCliOutput(raw);
} catch (err) {
console.error('[cli] raw output was not JSON:', JSON.stringify(raw.slice(0, 500)));
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
function looksLikeJson(s: string): boolean {
const t = s.trim();
return t.startsWith('{') || t.startsWith('[');
}
if (!looksLikeJson(raw)) {
throw new Error(`CLI emitted non-JSON stdout: ${raw.slice(0, 120)}`);
} Try / catch
try {
const res = parseServiceCliOutput(raw);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Failed to parse service CLI output as JSON')) {
console.error('[cli] raw stdout:', JSON.stringify(raw.slice(0, 500)));
// surface a CLI-level error, not a parse error, to the user
}
throw err;
} Prevention
- Always capture raw stdout on parse failure — the bytes explain the error.
- Keep service CLI logs on stderr so stdout stays pure JSON.
- Pin and verify the CLI binary version against the frontend wrapper.
When it happens
Trigger: The CLI binary prints errors/panics/banner text to stdout instead of pure JSON; RUST_LOG or tracing output pollutes stdout; the command fails at the shell level (missing binary, permission denied) so stdout is empty or carries shell diagnostics; the process is killed mid-write leaving truncated JSON.
Common situations: The service binary is missing from PATH or is a different major version than the wrapper expects; verbose logging configured to write to stdout; concurrent writes interleaving log lines into the JSON document; a half-written pipe read before process exit.
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
- Failed to parse service CLI output as JSON: parsed value doe
- Finish choosing how OpenHuman runs (tap Continue on the setu
- OpenHuman could not reach its remote (cloud) runtime. Check
- OpenHuman could not reach its local runtime. Quit and reopen
- audio blob is empty
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/8bcf8c4ba82460ce.
Report an issue: GitHub.