{"record":{"id":"eee7ebe3c1418d75","repo":"Hmbown/CodeWhale","slug":"codewhale-stream-json-line-line-number-was-not-a","errorCode":null,"errorMessage":"Codewhale stream-json line {line_number} was not an object","messagePattern":"Codewhale stream-json line (.+?) was not an object","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"integrations/verifiers-codewhale/codewhale_harness/harness.py","lineNumber":359,"sourceCode":"mv -f \"$install_dir/bin/.version.tmp\" \"$install_dir/bin/.version\"\n\"\"\"\n\n\ndef _parse_stream_receipt(stdout: str) -> dict[str, Any]:\n    counts: Counter[str] = Counter()\n    terminal: dict[str, Any] | None = None\n    ordered_types: list[str] = []\n    for line_number, line in enumerate(stdout.splitlines(), start=1):\n        if not line.strip():\n            continue\n        try:\n            event = json.loads(line)\n        except json.JSONDecodeError as error:\n            raise RuntimeError(\n                f\"Codewhale stream-json line {line_number} was not valid JSON\"\n            ) from error\n        if not isinstance(event, dict):\n            raise RuntimeError(\n                f\"Codewhale stream-json line {line_number} was not an object\"\n            )\n        if event.get(\"schema\") != STREAM_SCHEMA or event.get(\n            \"schema_version\"\n        ) != STREAM_SCHEMA_VERSION:\n            raise RuntimeError(\"Codewhale stream-json schema did not match v0.9.1\")\n        event_type = event.get(\"type\")\n        if event_type not in _EVENT_TYPES:\n            raise RuntimeError(\"Codewhale stream-json contained an unknown event type\")\n        counts[event_type] += 1\n        ordered_types.append(event_type)\n        if event_type == \"metadata\":\n            if terminal is not None:\n                raise RuntimeError(\"Codewhale emitted more than one terminal metadata receipt\")\n            meta = event.get(\"meta\")\n            if not isinstance(meta, dict) or meta.get(\"receipt_kind\") != \"terminal\":\n                raise RuntimeError(\"Codewhale metadata event was not a terminal receipt\")\n            terminal = _bounded_terminal(meta)","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/integrations/verifiers-codewhale/codewhale_harness/harness.py#L341-L377","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the reported line in the captured stdout to see which scalar/array the binary emitted.","Make the producer wrap every line as an event object with schema, schema_version, and a known type field.","Pin CodewhaleHarnessConfig.version to 0.9.1 or fix the custom binary_path facade to emit object events only.","Move any scalar telemetry (counts, codes) into the metadata/done event payloads instead of standalone lines."],"exampleFix":"# before: wrapper writes a scalar per line\nsys.stdout.write(json.dumps(len(events)) + \"\\n\")\n\n# after: wrapper writes a well-formed event object\nsys.stdout.write(json.dumps({\"schema\": \"codewhale.exec-stream\", \"schema_version\": 1, \"type\": \"done\"}) + \"\\n\")","handlingStrategy":"type-guard","validationCode":"import json\n\ndef stream_lines_are_objects(stdout: str) -> bool:\n    for line in stdout.splitlines():\n        if not line.strip():\n            continue\n        value = json.loads(line)  # raises on invalid JSON; check separately\n        if not isinstance(value, dict):\n            return False\n    return True","typeGuard":"def is_stream_event(value: object) -> bool:\n    return (\n        isinstance(value, dict)\n        and value.get(\"schema\") == \"codewhale.exec-stream\"\n        and value.get(\"schema_version\") == 1\n        and isinstance(value.get(\"type\"), str)\n    )","tryCatchPattern":"except RuntimeError as error:\n    if \"was not an object\" in str(error):\n        # inspect the named line and fix the producer; do not retry blindly\n        log_stream_for_debug(stdout)\n    raise","preventionTips":["Emit only object events from facades; never bare json.dumps of scalars or arrays.","Wrap scalar telemetry inside a well-typed event object.","Add a facade unit test asserting every stdout line json.loads to a dict with the schema envelope."],"tags":["json","parsing","validation","stream-json","codewhale-harness"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}