{"record":{"id":"1cc173f94d8af87d","repo":"unslothai/unsloth","slug":"expected-numbers-got-a-boolean","errorCode":null,"errorMessage":"Expected numbers, got a boolean.","messagePattern":"Expected numbers, got a boolean\\.","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"warning","filePath":"studio/backend/routes/settings.py","lineNumber":736,"sourceCode":"        \"custom_context_length\",\n        \"spec_draft_n_max\",\n        \"n_parallel\",\n        \"n_batch\",\n        \"n_ubatch\",\n        \"gpu_layers\",\n        \"n_cpu_moe\",\n        \"gpu_ids\",\n        mode = \"before\",\n    )\n    @classmethod\n    def _no_booleans(cls, value: Any) -> Any:\n        # bool subclasses int and pydantic parses non-strictly, so `true` arrives as 1: a\n        # payload could pin GPU 1 or set a one-token context. _bounded_int rejects bools but\n        # never sees one, since coercion happens here first. Only bools, so lax parsing stays.\n        if isinstance(value, bool):\n            raise ValueError(\"Expected a number, got a boolean.\")\n        if isinstance(value, list) and any(isinstance(item, bool) for item in value):\n            raise ValueError(\"Expected numbers, got a boolean.\")\n        return value\n\n\nclass ModelOverridesResponse(BaseModel):\n    overrides: dict[str, dict]\n    # Filled only when the caller named a model: the entry ITS load would apply,\n    # resolved here rather than in the browser. The folding rules are Python's\n    # (casefold is not toLowerCase, and an ambiguous fold matches nothing on\n    # purpose), so a client mirroring them can only approximate.\n    resolved: Optional[dict] = None\n    resolved_key: Optional[str] = None\n\n\ndef _upload_limit_response(limit_mb: int) -> UploadLimitResponse:\n    return UploadLimitResponse(\n        max_upload_size_mb = limit_mb,\n        max_upload_size_bytes = upload_limit_bytes(limit_mb),\n        max_upload_size_label = upload_limit_label(limit_mb),","sourceCodeStart":718,"sourceCodeEnd":754,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/settings.py#L718-L754","documentation":"Pydantic validation failure (HTTP 422) from the same _no_booleans before-validator, list branch: the field value is a list (in practice gpu_ids) and at least one element is a boolean. Without the guard, [true, 1] would coerce to [1, 1] and silently duplicate GPU 1 instead of 400/422.","triggerScenarios":"PUT model overrides with gpu_ids: [true] or [0, false, 2]; UI checkboxes feeding directly into the GPU id array.","commonSituations":"Checkbox-driven GPU selector where checked=true is pushed into the ids array; mixed data from form state; scripted payloads reusing toggle state.","solutions":["Send only integers in gpu_ids, e.g. [0, 1].","Map checkbox state to indices client-side: gpus.map((checked, i) => checked ? i : null).filter(v => v !== null).","Assert every element Number.isInteger before submit."],"exampleFix":"// before\nawait api.put(url, { gpu_ids: [true, false] }); // 422\n\n// after\nconst gpuIds = gpus.map((checked, i) => (checked ? i : -1)).filter(i => i >= 0);\nif (!gpuIds.every(Number.isInteger)) throw new TypeError('gpu_ids must be integers');\nawait api.put(url, { gpu_ids: gpuIds });","handlingStrategy":"type-guard","validationCode":"if (Array.isArray(payload.gpu_ids) && payload.gpu_ids.some(Number.isBoolean)) {\n  throw new TypeError('gpu_ids must contain only integers');\n}","typeGuard":"function isGpuIdList(v: unknown): v is number[] {\n  return Array.isArray(v) && v.every(n => Number.isInteger(n) && n >= 0);\n}","tryCatchPattern":"try { await api.put(overridesUrl, payload); }\ncatch (e) {\n  if (e.status === 422 && /Expected numbers/.test(e.detail?.toString() ?? '')) { payload.gpu_ids = payload.gpu_ids.filter(Number.isInteger); return api.put(overridesUrl, payload); }\n  throw e;\n}","preventionTips":["Map checkbox state to GPU indices, not the booleans themselves.","Filter arrays through Number.isInteger before submit.","Add unit tests asserting gpu_ids contains no booleans."],"tags":["pydantic","validation","http-422","boolean-coercion","gpu-ids","lists"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}