unslothai/unsloth · warning · ValueError
Expected a number, got a boolean.
Error message
Expected a number, got a boolean.
What it means
Pydantic validation failure (HTTP 422) from a mode='before' validator applied to the numeric override fields (max_seq_length, custom_context_length, spec_draft_n_max, n_parallel, n_batch, n_ubatch, gpu_layers, n_cpu_moe). Because bool subclasses int, lax pydantic parsing would turn JSON true into 1 — pinning GPU 1 or setting a one-token context. The validator rejects any scalar bool before coercion; downstream _bounded_int never sees one.
Source
Thrown at studio/backend/routes/settings.py:734
@field_validator(
"max_seq_length",
"custom_context_length",
"spec_draft_n_max",
"n_parallel",
"n_batch",
"n_ubatch",
"gpu_layers",
"n_cpu_moe",
"gpu_ids",
mode = "before",
)
@classmethod
def _no_booleans(cls, value: Any) -> Any:
# bool subclasses int and pydantic parses non-strictly, so `true` arrives as 1: a
# payload could pin GPU 1 or set a one-token context. _bounded_int rejects bools but
# never sees one, since coercion happens here first. Only bools, so lax parsing stays.
if isinstance(value, bool):
raise ValueError("Expected a number, got a boolean.")
if isinstance(value, list) and any(isinstance(item, bool) for item in value):
raise ValueError("Expected numbers, got a boolean.")
return value
class ModelOverridesResponse(BaseModel):
overrides: dict[str, dict]
# Filled only when the caller named a model: the entry ITS load would apply,
# resolved here rather than in the browser. The folding rules are Python's
# (casefold is not toLowerCase, and an ambiguous fold matches nothing on
# purpose), so a client mirroring them can only approximate.
resolved: Optional[dict] = None
resolved_key: Optional[str] = None
def _upload_limit_response(limit_mb: int) -> UploadLimitResponse:
return UploadLimitResponse(
max_upload_size_mb = limit_mb,View on GitHub (pinned to 203007d190)
Solutions
- Send integers for all numeric fields (e.g. gpu_layers: 40, n_parallel: 2); use null/omit to leave a field unchanged.
- Add a client-side typeof check rejecting booleans for these keys.
- Validate generated payloads against a JSON schema with type:'integer' before submitting.
Example fix
// before
await api.put(url, { gpu_layers: true }); // 422
// after
const overrides = { gpu_layers: Number(gpuLayersInput.value) };
for (const [k, v] of Object.entries(overrides)) {
if (typeof v === 'boolean') throw new TypeError(`${k} must be a number`);
}
await api.put(url, overrides); Defensive patterns
Strategy: type-guard
Validate before calling
const NUMERIC_KEYS = ['max_seq_length','custom_context_length','spec_draft_n_max','n_parallel','n_batch','n_ubatch','gpu_layers','n_cpu_moe'];
for (const k of NUMERIC_KEYS) {
if (typeof payload[k] === 'boolean') throw new TypeError(`${k} must be a number`);
} Type guard
function isNumericOverride(v: unknown): v is number {
return typeof v === 'number' && Number.isInteger(v);
} Try / catch
try { await api.put(overridesUrl, payload); }
catch (e) {
if (e.status === 422 && /boolean/.test(e.detail?.toString() ?? '')) { sanitizeNumericFields(); return; }
throw e;
} Prevention
- Use number inputs (<input type=number>) for these fields, never toggles.
- Validate payload shape against the OpenAPI schema before sending.
- Remember null/omitted means 'leave unchanged'; true is never a valid value.
When it happens
Trigger: PUT model overrides with gpu_layers: true, n_parallel: false, or any of the listed fields set to a JSON boolean instead of an integer.
Common situations: JS UI binding a toggle to a numeric field; config generated from YAML where 'on'/'off' became true/false; scripts building payloads dynamically with mixed types.
Related errors
- fraction must be a number, not a boolean
- Expected numbers, got a boolean.
- Provide either content_base64 or file_ids, not both
- Provide either content_base64 or file_ids
- file_ids must not be empty
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/d49362e0be846212.
Report an issue: GitHub.