ZhuLinsen/daily_stock_analysis · error · LocalCliExtractionError

schema_validation_failed

schema_validation_failed

Error message

schema_validation_failed

What it means

A structured LocalCliExtractionError with code schema_validation_failed, raised while parsing the newline-delimited JSON event stream from the local CLI (opencode) backend. Every decoded event must be a JSON object; a non-object (string, number, list element, null) at a given event_index fails validation. It indicates the child process emitted a payload that does not conform to the expected event protocol.

Source

Thrown at src/llm/local_cli_backend.py:1980

                GenerationErrorCode.INVALID_JSON,
                "invalid_json",
                details={"error": "json_decoder_made_no_progress"},
            )
        index = next_index

        if isinstance(decoded, list):
            for item in decoded:
                event_index += 1
                yield _validate_opencode_event(item, event_index=event_index)
            continue

        event_index += 1
        yield _validate_opencode_event(decoded, event_index=event_index)


def _validate_opencode_event(value: Any, *, event_index: int) -> Dict[str, Any]:
    if not isinstance(value, dict):
        raise LocalCliExtractionError(
            GenerationErrorCode.SCHEMA_VALIDATION_FAILED,
            "schema_validation_failed",
            details={"event_index": event_index, "expected": "object_event"},
        )
    event_type = value.get("type")
    if not isinstance(event_type, str) or not event_type.strip():
        raise LocalCliExtractionError(
            GenerationErrorCode.SCHEMA_VALIDATION_FAILED,
            "schema_validation_failed",
            details={"event_index": event_index, "expected": "event_type"},
        )
    return value


def _opencode_blocked_event_reason(event: Dict[str, Any], event_type_lower: str) -> str:
    if (
        event_type_lower in _OPENCODE_BLOCKED_EVENT_TYPES
        or any(blocked in event_type_lower for blocked in _OPENCODE_BLOCKED_EVENT_TYPES)

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Reproduce the raw subprocess output (run the same CLI command manually) and inspect which line is a non-object JSON value
  2. Pin or upgrade the local CLI to the version whose event protocol this backend expects
  3. Ensure nothing else writes to the captured stdout of the subprocess (redirect debug logs to stderr)
  4. If you wrap the CLI in a script, make the wrapper only emit newline-delimited JSON objects with a string 'type' field
Defensive patterns

Strategy: try-catch

Validate before calling

import json

def is_object_event(line: str) -> bool:
    try:
        value = json.loads(line)
    except json.JSONDecodeError:
        return False
    return isinstance(value, dict)

Type guard

from typing import Any, Dict

def is_opencode_event(value: Any) -> "typeguard":
    return isinstance(value, dict) and isinstance(value.get("type"), str) and bool(value.get("type").strip())

Try / catch

try:
    for event in stream_opencode_events(proc):
        handle(event)
except LocalCliExtractionError as exc:
    if exc.error_code == GenerationErrorCode.SCHEMA_VALIDATION_FAILED:
        log.error("CLI event protocol violation: %s", exc.details)
        capture_raw_stream_for_diagnosis()
    raise

Prevention

When it happens

Trigger: The CLI subprocess prints a bare JSON scalar or string line (e.g. "\"done\"" or "42") into stdout that is captured as an event; a list payload containing non-object items; any third-party tool, progress bar, or stray log line interleaved into the event stream that decodes to a non-object JSON value.

Common situations: The local CLI version changed its output protocol; another tool writes to the same stdout; debug logging accidentally routed to stdout; a wrapper script echoing JSON fragments; malformed partial lines after a truncated buffer flush.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/5ffd30c1ed43d5c0. Report an issue: GitHub.