{"record":{"id":"1f37a3c3e1e46e42","repo":"unslothai/unsloth","slug":"expected-a-number-got-a-boolean","errorCode":null,"errorMessage":"Expected a number, got a boolean.","messagePattern":"Expected a number, got a boolean\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/models/inference.py","lineNumber":246,"sourceCode":"        description = (\n            \"Manual mode only: relative share of the model per GPU (--tensor-split), \"\n            \"in the order of the GPUs in use, e.g. [2, 1] for 2:1. Omit it to let \"\n            \"llama.cpp use its default, which splits by free VRAM. Any list given is \"\n            \"passed through as-is, so send [1, 1] to force an even split. Ignored \"\n            \"unless gpu_memory_mode is 'manual' with gpu_layers >= 0.\"\n        ),\n    )\n\n    @field_validator(\"n_batch\", \"n_ubatch\", mode = \"before\")\n    @classmethod\n    def _no_booleans(cls, value: Any) -> Any:\n        # bool subclasses int and pydantic parses non-strictly, so `true` arrives as 1 and\n        # the load launches --batch-size 1, which llama-server aborts on: a 500 rather than\n        # a 422. Mirrors ModelOverrideRequest._no_booleans so /load and /settings agree.\n        # Kept off the annotation: an Annotated BeforeValidator stops the Field constraints\n        # folding into the int core schema, and they leak into OpenAPI as ge/le.\n        if isinstance(value, bool):\n            raise ValueError(\"Expected a number, got a boolean.\")\n        return value\n\n    @field_validator(\"tensor_split\")\n    @classmethod\n    def _reject_degenerate_tensor_split(cls, value: Optional[List[float]]) -> Optional[List[float]]:\n        # A negative / non-finite / all-zero split is silently dropped at launch\n        # (stored as None) yet still compared raw in the reload dedupe, so an\n        # identical Apply reloads forever. Reject it up front; [] = no split.\n        if not value:\n            return value\n        import math\n\n        if any((not math.isfinite(v)) or v < 0 for v in value):\n            raise ValueError(\"tensor_split entries must be finite and non-negative\")\n        if sum(value) <= 0:\n            raise ValueError(\"tensor_split must have a positive total\")\n        return value\n","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/inference.py#L228-L264","documentation":"Raised by a mode='before' field_validator on n_batch/n_ubatch when the incoming JSON value is a Python bool. Because bool subclasses int and Pydantic parses non-strictly, `true` would silently coerce to 1 and the load would launch llama-server with --batch-size 1, which aborts and surfaces as a 500. The validator converts that into a clean 422 at request time.","triggerScenarios":"Sending {\"n_batch\": true} or {\"n_ubatch\": false} (or a YAML/env-derived value that a config layer typed as bool) to /load or /settings. The same guard exists on ModelOverrideRequest so both endpoints agree.","commonSituations":"Hand-written JSON configs where true was used instead of 1; templating engines that render booleans for numeric options; YAML 1.1 parsers coercing 'yes'/'no' to bool; client code doing `n_batch: use_fast and 512` style expressions that evaluate to a bool.","solutions":["Change the JSON payload to a real integer, e.g. \"n_batch\": 512 instead of true.","Fix the client-side config typing so numeric knobs are ints, not bools.","If the value comes from YAML/env, coerce explicitly with int(value) after a bool check."],"exampleFix":"# before\ncfg = {\"n_batch\": True}  # coerced to 1 -> llama-server abort\n\n# after\ncfg = {\"n_batch\": 512}","handlingStrategy":"type-guard","validationCode":"def normalize_int_field(value):\n    if isinstance(value, bool):\n        raise TypeError('boolean where an integer is required')\n    return int(value)","typeGuard":"def is_plain_int(v) -> bool:\n    return isinstance(v, int) and not isinstance(v, bool)","tryCatchPattern":null,"preventionTips":["Never let config layers type numeric knobs as bool","In YAML configs quote ambiguous scalars or set the numeric type explicitly","Lint request payloads: flag bool values in fields documented as numbers"],"tags":["pydantic","validation","type-confusion","boolean","llama-server"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}