ruvnet/RuView · error · ValueError
{path}: expected {{'nodes': [...]}} or a top-level list
Error message
{path}: expected {{'nodes': [...]}} or a top-level list What it means
load_geometry_file reads the transceiver-geometry JSON passed via --geometry-file to calibrate-camera-room.py. It expects a document shaped {'nodes': [...]} (a 'nodes' key when the top level is an object); if no node list can be extracted it raises ValueError stating the expected shape. Each element is then validated for 'id' and 'position_m'.
Source
Thrown at scripts/calibrate-camera-room.py:169
node = {"id": parts[0], "position_m": [float(parts[1]), float(parts[2]), float(parts[3])]}
if len(parts) == 5:
node["antenna_yaw_deg"] = float(parts[4])
except ValueError:
print(" positions must be numeric", file=sys.stderr)
continue
nodes.append(node)
if not nodes:
print("WARNING: no transceiver nodes entered; bundle will carry empty geometry.",
file=sys.stderr)
return {"nodes": nodes, "units": "meters", "source": "tape-measure-prompt"}
def load_geometry_file(path: Path) -> dict:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
nodes = data.get("nodes", data if isinstance(data, list) else None)
if nodes is None:
raise ValueError(f"{path}: expected {{'nodes': [...]}} or a top-level list")
for node in nodes:
if "id" not in node or "position_m" not in node:
raise ValueError(f"{path}: each node needs 'id' and 'position_m' [x,y,z]")
return {"nodes": nodes, "units": "meters", "source": "file"}
def main():
parser = argparse.ArgumentParser(
description="Two-checkerboard camera-room calibration (ADR-152 S2.1.3 / ADR-079)."
)
parser.add_argument("--wall-image", required=True,
help="Photo of the checkerboard on the origin wall")
parser.add_argument("--floor-image", required=True,
help="Photo of the checkerboard on the floor (camera NOT moved)")
parser.add_argument("--wall-origin", type=parse_vec3, default="0.5,0.0,1.6",
help="Room xyz (m) of the wall board's first inner corner "
"(default: 0.5,0.0,1.6)")
parser.add_argument("--floor-origin", type=parse_vec3, default="1.0,1.0,0.0",View on GitHub (pinned to 4685618388)
Solutions
- Rewrite the JSON as {"nodes": [{"id": "n1", "position_m": [x, y, z]}, ...]} with positions in meters
- Or omit --geometry-file and use the interactive tape-measure prompt instead
- Pre-validate the file: jq 'has("nodes")' nodes.json
Example fix
// before
// nodes.json
{"anchors": [{"id": "n1", "position_m": [0, 0, 1]}]}
// after
// nodes.json
{"nodes": [{"id": "n1", "position_m": [0.0, 0.0, 1.0]}]} Defensive patterns
Strategy: validation
Validate before calling
import json
def valid_geometry_file(path: str) -> bool:
with open(path, encoding="utf-8") as f:
data = json.load(f)
return isinstance(data, dict) and isinstance(data.get("nodes"), list)
if not valid_geometry_file("nodes.json"):
raise SystemExit("nodes.json must be {'nodes': [...]} or omit --geometry-file") Try / catch
try:
geom = load_geometry_file(path)
except ValueError as e:
raise SystemExit(f"bad geometry file: {e}") from e Prevention
- Generate geometry files from one canonical template
- Validate the JSON shape with jq or a loader before invoking the calibration script
- Prefer the interactive prompt for first runs, then save its output as the file
When it happens
Trigger: Passing --geometry-file with a JSON object that lacks the 'nodes' key — e.g. {'anchors': [...]}, {'transceivers': [...]}, or a full calibration bundle — so `data.get("nodes", ...)` yields None.
Common situations: Hand-written room geometry files; exporting from a CAD or notes tool that names the array differently; feeding an intrinsics file where a geometry file was expected.
Related errors
- Failed to load domain configuration: {e}
- invalid privacy class {s!r}; expected one of {list(m.keys())
- {path}: each node needs 'id' and 'position_m' [x,y,z]
- Invalid axis token {token!r}; expected one of {sorted(_AXIS_
- Intrinsics file {path} missing key {key!r}
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/fb8c7448204d2a46.
Report an issue: GitHub.