{"record":{"id":"0c80c178f282be03","repo":"ruvnet/RuView","slug":"invalid-axis-token-token-r-expected-one-of-sor","errorCode":null,"errorMessage":"Invalid axis token {token!r}; expected one of {sorted(_AXIS_TOKENS)}","messagePattern":"Invalid axis token (.+?); expected one of (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/calibration_lib.py","lineNumber":61,"sourceCode":"\n# Default checkerboard: 9x6 inner corners, 25 mm squares (a common print).\nDEFAULT_BOARD_COLS = 9\nDEFAULT_BOARD_ROWS = 6\nDEFAULT_SQUARE_SIZE_MM = 25.0\n\n_AXIS_TOKENS = {\n    \"+x\": (1.0, 0.0, 0.0), \"-x\": (-1.0, 0.0, 0.0),\n    \"+y\": (0.0, 1.0, 0.0), \"-y\": (0.0, -1.0, 0.0),\n    \"+z\": (0.0, 0.0, 1.0), \"-z\": (0.0, 0.0, -1.0),\n}\n\n\ndef parse_axis(token: str) -> np.ndarray:\n    \"\"\"Parse an axis token like '+x' or '-z' into a room-frame unit vector.\"\"\"\n    key = token.strip().lower()\n    if key in _AXIS_TOKENS:\n        return np.array(_AXIS_TOKENS[key], dtype=np.float64)\n    raise ValueError(f\"Invalid axis token {token!r}; expected one of {sorted(_AXIS_TOKENS)}\")\n\n\n# ---------------------------------------------------------------------------\n# Checkerboard geometry\n# ---------------------------------------------------------------------------\n\ndef board_object_points(cols: int, rows: int, square_size_m: float) -> np.ndarray:\n    \"\"\"Inner-corner positions in the board's own frame (z=0 plane), row-major.\n\n    Matches the corner ordering of cv2.findChessboardCorners for a\n    (cols, rows) pattern: cols varies fastest.\n    \"\"\"\n    pts = np.zeros((rows * cols, 3), dtype=np.float64)\n    grid = np.mgrid[0:cols, 0:rows].T.reshape(-1, 2)  # (rows*cols, 2), cols fastest\n    pts[:, :2] = grid * square_size_m\n    return pts\n\n","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/scripts/calibration_lib.py#L43-L79","documentation":"parse_axis converts a signed-axis token into a room-frame unit vector for calibration options. Tokens are matched after strip().lower(), so case and surrounding whitespace are tolerated, but the sign is mandatory: only +x, -x, +y, -y, +z, -z are accepted. Anything else raises ValueError listing the sorted valid tokens.","triggerScenarios":"Passing 'x' or 'Z' (missing sign), or words like 'up'/'north', to any CLI option parsed by parse_axis (e.g. --board-normal, axis options of the calibration scripts).","commonSituations":"Assuming a bare axis letter works; using ceiling/floor vocabulary instead of signed room-frame axes; stray punctuation in config values.","solutions":["Use a signed token: one of +x -x +y -y +z -z (case-insensitive)","Map descriptive words to signed axes in your wrapper (up → +z, west → -x) before invoking the script","Copy the accepted set from the error message, which prints sorted(_AXIS_TOKENS)"],"exampleFix":"# before\npython calibrate.py --board-normal z  # ValueError: Invalid axis token 'z'\n\n# after\npython calibrate.py --board-normal +z","handlingStrategy":"type-guard","validationCode":"AXIS_TOKENS = {\"+x\", \"-x\", \"+y\", \"-y\", \"+z\", \"-z\"}\n\nif axis_arg.strip().lower() not in AXIS_TOKENS:\n    raise SystemExit(f\"axis must be one of {sorted(AXIS_TOKENS)}\")","typeGuard":"def is_valid_axis_token(token: str) -> bool:\n    return isinstance(token, str) and token.strip().lower() in {\n        \"+x\", \"-x\", \"+y\", \"-y\", \"+z\", \"-z\"\n    }","tryCatchPattern":"try:\n    axis = parse_axis(token)\nexcept ValueError as e:\n    raise SystemExit(f\"bad axis argument: {e}\") from e","preventionTips":["Document signed-axis tokens in wrapper scripts and help text","Normalize descriptive words (up/down) to signed tokens in your own CLI layer","Validate config axes at startup, not at first calibration use"],"tags":["python","parsing","cli","validation","calibration","axes"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}