Hmbown/CodeWhale · error · RuntimeError

Codewhale stream-json line {line_number} was not an object

Error message

Codewhale stream-json line {line_number} was not an object

What it means

Each non-blank stdout line from Codewhale must parse as JSON and then as a JSON object (dict). This error means the line was valid JSON but a scalar, array, or null — for example the string "42", "null", or a bare quoted string. It carries the offending line number so you can locate the exact event.

Source

Thrown at integrations/verifiers-codewhale/codewhale_harness/harness.py:359

mv -f "$install_dir/bin/.version.tmp" "$install_dir/bin/.version"
"""


def _parse_stream_receipt(stdout: str) -> dict[str, Any]:
    counts: Counter[str] = Counter()
    terminal: dict[str, Any] | None = None
    ordered_types: list[str] = []
    for line_number, line in enumerate(stdout.splitlines(), start=1):
        if not line.strip():
            continue
        try:
            event = json.loads(line)
        except json.JSONDecodeError as error:
            raise RuntimeError(
                f"Codewhale stream-json line {line_number} was not valid JSON"
            ) from error
        if not isinstance(event, dict):
            raise RuntimeError(
                f"Codewhale stream-json line {line_number} was not an object"
            )
        if event.get("schema") != STREAM_SCHEMA or event.get(
            "schema_version"
        ) != STREAM_SCHEMA_VERSION:
            raise RuntimeError("Codewhale stream-json schema did not match v0.9.1")
        event_type = event.get("type")
        if event_type not in _EVENT_TYPES:
            raise RuntimeError("Codewhale stream-json contained an unknown event type")
        counts[event_type] += 1
        ordered_types.append(event_type)
        if event_type == "metadata":
            if terminal is not None:
                raise RuntimeError("Codewhale emitted more than one terminal metadata receipt")
            meta = event.get("meta")
            if not isinstance(meta, dict) or meta.get("receipt_kind") != "terminal":
                raise RuntimeError("Codewhale metadata event was not a terminal receipt")
            terminal = _bounded_terminal(meta)

View on GitHub (pinned to 8880682c63)

Solutions

  1. Inspect the reported line in the captured stdout to see which scalar/array the binary emitted.
  2. Make the producer wrap every line as an event object with schema, schema_version, and a known type field.
  3. Pin CodewhaleHarnessConfig.version to 0.9.1 or fix the custom binary_path facade to emit object events only.
  4. Move any scalar telemetry (counts, codes) into the metadata/done event payloads instead of standalone lines.

Example fix

# before: wrapper writes a scalar per line
sys.stdout.write(json.dumps(len(events)) + "\n")

# after: wrapper writes a well-formed event object
sys.stdout.write(json.dumps({"schema": "codewhale.exec-stream", "schema_version": 1, "type": "done"}) + "\n")
Defensive patterns

Strategy: type-guard

Validate before calling

import json

def stream_lines_are_objects(stdout: str) -> bool:
    for line in stdout.splitlines():
        if not line.strip():
            continue
        value = json.loads(line)  # raises on invalid JSON; check separately
        if not isinstance(value, dict):
            return False
    return True

Type guard

def is_stream_event(value: object) -> bool:
    return (
        isinstance(value, dict)
        and value.get("schema") == "codewhale.exec-stream"
        and value.get("schema_version") == 1
        and isinstance(value.get("type"), str)
    )

Try / catch

except RuntimeError as error:
    if "was not an object" in str(error):
        # inspect the named line and fix the producer; do not retry blindly
        log_stream_for_debug(stdout)
    raise

Prevention

When it happens

Trigger: A facade or Codewhale build emits JSON scalars on stdout: printing a token count, an exit code, or a JSON-encoded array where the harness expects one event object per line. Also happens when a wrapper json.dumps()s a non-dict value (str/int/list) into the stream.

Common situations: Custom binary_path wrappers that log values via json.dumps(value) instead of json.dumps(event_dict); versions of Codewhale that emit NDJSON status scalars; test stubs returning e.g. '"ok"' per line.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/eee7ebe3c1418d75. Report an issue: GitHub.