ruvnet/RuView · error · ValueError

Invalid axis token {token!r}; expected one of {sorted(_AXIS_

Error message

Invalid axis token {token!r}; expected one of {sorted(_AXIS_TOKENS)}

What it means

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.

Source

Thrown at scripts/calibration_lib.py:61

# Default checkerboard: 9x6 inner corners, 25 mm squares (a common print).
DEFAULT_BOARD_COLS = 9
DEFAULT_BOARD_ROWS = 6
DEFAULT_SQUARE_SIZE_MM = 25.0

_AXIS_TOKENS = {
    "+x": (1.0, 0.0, 0.0), "-x": (-1.0, 0.0, 0.0),
    "+y": (0.0, 1.0, 0.0), "-y": (0.0, -1.0, 0.0),
    "+z": (0.0, 0.0, 1.0), "-z": (0.0, 0.0, -1.0),
}


def parse_axis(token: str) -> np.ndarray:
    """Parse an axis token like '+x' or '-z' into a room-frame unit vector."""
    key = token.strip().lower()
    if key in _AXIS_TOKENS:
        return np.array(_AXIS_TOKENS[key], dtype=np.float64)
    raise ValueError(f"Invalid axis token {token!r}; expected one of {sorted(_AXIS_TOKENS)}")


# ---------------------------------------------------------------------------
# Checkerboard geometry
# ---------------------------------------------------------------------------

def board_object_points(cols: int, rows: int, square_size_m: float) -> np.ndarray:
    """Inner-corner positions in the board's own frame (z=0 plane), row-major.

    Matches the corner ordering of cv2.findChessboardCorners for a
    (cols, rows) pattern: cols varies fastest.
    """
    pts = np.zeros((rows * cols, 3), dtype=np.float64)
    grid = np.mgrid[0:cols, 0:rows].T.reshape(-1, 2)  # (rows*cols, 2), cols fastest
    pts[:, :2] = grid * square_size_m
    return pts

View on GitHub (pinned to 4685618388)

Solutions

  1. Use a signed token: one of +x -x +y -y +z -z (case-insensitive)
  2. Map descriptive words to signed axes in your wrapper (up → +z, west → -x) before invoking the script
  3. Copy the accepted set from the error message, which prints sorted(_AXIS_TOKENS)

Example fix

# before
python calibrate.py --board-normal z  # ValueError: Invalid axis token 'z'

# after
python calibrate.py --board-normal +z
Defensive patterns

Strategy: type-guard

Validate before calling

AXIS_TOKENS = {"+x", "-x", "+y", "-y", "+z", "-z"}

if axis_arg.strip().lower() not in AXIS_TOKENS:
    raise SystemExit(f"axis must be one of {sorted(AXIS_TOKENS)}")

Type guard

def is_valid_axis_token(token: str) -> bool:
    return isinstance(token, str) and token.strip().lower() in {
        "+x", "-x", "+y", "-y", "+z", "-z"
    }

Try / catch

try:
    axis = parse_axis(token)
except ValueError as e:
    raise SystemExit(f"bad axis argument: {e}") from e

Prevention

When it happens

Trigger: 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).

Common situations: Assuming a bare axis letter works; using ceiling/floor vocabulary instead of signed room-frame axes; stray punctuation in config values.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/0c80c178f282be03. Report an issue: GitHub.