{"record":{"id":"c60ba7f6546f85c8","repo":"can1357/oh-my-pi","slug":"field-must-be-an-integer","errorCode":null,"errorMessage":"{field} must be an integer","messagePattern":"(.+?) must be an integer","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/omp-rpc/src/omp_rpc/protocol.py","lineNumber":263,"sourceCode":"        return tuple(items)\n    raise ValueError(f\"{field} must be a string or an array of strings\")\n\n\ndef _optional_bool(payload: JsonObject, field: str) -> bool | None:\n    value = payload.get(field)\n    if value is None:\n        return None\n    if not isinstance(value, bool):\n        raise ValueError(f\"{field} must be a boolean\")\n    return value\n\n\ndef _optional_int(payload: JsonObject, field: str) -> int | None:\n    value = payload.get(field)\n    if value is None:\n        return None\n    if isinstance(value, bool) or not isinstance(value, int):\n        raise ValueError(f\"{field} must be an integer\")\n    return value\n\n\ndef _optional_float(payload: JsonObject, field: str) -> float | None:\n    value = payload.get(field)\n    if value is None:\n        return None\n    if isinstance(value, bool) or not isinstance(value, (int, float)):\n        raise ValueError(f\"{field} must be a number\")\n    return float(value)\n\n\ndef _tuple_of_strings(values: object, *, field: str) -> tuple[str, ...] | None:\n    if values is None:\n        return None\n    if not isinstance(values, list):\n        raise ValueError(f\"{field} must be a list\")\n","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/omp-rpc/src/omp_rpc/protocol.py#L245-L281","documentation":"`_optional_int` accepts absence but otherwise demands a JSON integer; it explicitly rejects booleans (checked first because bool subclasses int in Python) and rejects floats and strings. This keeps counts/indices/ids strictly integral.","triggerScenarios":"parse_assistant_message_event, parse_bash_result, parse_extension_ui_request, or parse_notification receives a field like an exit code, token count, or message index as a float (3.0), a numeric string (\"42\"), or true/false.","commonSituations":"A producer serializes integers as floats (JavaScript numbers can arrive as 3.0 via some serializers); values passed through shell/CSV round-trips become strings; a boolean flag was put in an int field.","solutions":["Coerce to int before parsing: int(float(value)) for floats, int(value) for numeric strings","Fix the producer to emit JSON integers without decimal points","Verify the key holds the intended field — a swapped key often explains a type surprise"],"exampleFix":"# before\npayload = {\"exit_code\": \"0\"}\nresult = parse_bash_result(payload)  # ValueError: exit_code must be an integer\n# after\npayload = {\"exit_code\": int(\"0\")}\nresult = parse_bash_result(payload)","handlingStrategy":"validation","validationCode":"def coerce_optional_int(payload: dict, field: str) -> dict:\n    value = payload.get(field)\n    if value is not None and not isinstance(value, bool):\n        if isinstance(value, float) and value.is_integer():\n            payload[field] = int(value)\n        elif isinstance(value, str) and value.lstrip(\"-\").isdigit():\n            payload[field] = int(value)\n    if payload.get(field) is not None and not isinstance(payload[field], int):\n        raise TypeError(f\"{field!r} must be an integer, got {value!r}\")\n    return payload\n\nparse_bash_result(coerce_optional_int(payload, \"exit_code\"))","typeGuard":"def is_optional_int(payload: dict, field: str) -> bool:\n    value = payload.get(field)\n    return value is None or (isinstance(value, int) and not isinstance(value, bool))","tryCatchPattern":"try:\n    result = parse_bash_result(payload)\nexcept ValueError as e:\n    logger.warning(\"bash result had non-integer numeric field\", extra={\"error\": str(e)})\n    result = None","preventionTips":["Ensure serializers emit whole numbers without decimal points (JS numbers via JSON can become floats)","Never route integers through string formatting (shell, CSV) before sending","Explicitly exclude bools from int fields — check isinstance(value, bool) first","Capture and diff real payloads when upgrading the server"],"tags":["python","rpc","type-validation","integer"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}