{"record":{"id":"ba6c21b339d9e609","repo":"unslothai/unsloth","slug":"convrot-group-size-must-be-a-power-of-4-got-grou","errorCode":null,"errorMessage":"ConvRot group size must be a power of 4, got {group_size!r}","messagePattern":"ConvRot group size must be a power of 4, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/diffusion_convrot.py","lineNumber":309,"sourceCode":"            continue\n        (rotatable if module.in_features % group_size == 0 else not_divisible).append(fqn)\n    return tuple(rotatable), tuple(not_divisible)\n\n\ndef rotate_linears_(\n    transformer: Any,\n    fqns: Iterable[str],\n    group_size: int = DEFAULT_CONVROT_GROUPSIZE,\n) -> tuple[str, ...]:\n    \"\"\"OFFLINE half: rotate the weights of ``fqns`` and install the online rotation on each.\n\n    Call BEFORE ``quantize_``, on a dense model: the whole point is that the quantizer sees the\n    flatter distribution. Returns the fqns rotated, in the order given. Raises on anything it\n    cannot rotate, so a builder can never record a set larger than the one it actually applied.\"\"\"\n    from torch import nn\n\n    if not is_power_of_four(group_size):\n        raise ValueError(f\"ConvRot group size must be a power of 4, got {group_size!r}\")\n    modules = dict(transformer.named_modules())\n    rotated: list[str] = []\n    for fqn in fqns:\n        module = modules.get(fqn)\n        if not isinstance(module, nn.Linear):\n            raise ValueError(f\"cannot rotate {fqn!r}: not an nn.Linear on this model\")\n        rotate_convrot_weight_(module, group_size)\n        _install_rotation(module, group_size)\n        rotated.append(fqn)\n    return tuple(rotated)\n\n\ndef apply_activation_rotation(\n    transformer: Any,\n    metadata: Any,\n    *,\n    logger: Any = None,\n) -> tuple[str, ...]:","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/diffusion_convrot.py#L291-L327","documentation":"The offline rotation entry point validates group_size up front: the Hadamard construction only exists for powers of four, so anything else is refused before any module is touched. This is the build-time twin of the check in build_convrot_hadamard().","triggerScenarios":"Calling the offline rotate-weights function (apply_rotation) with a group_size that fails is_power_of_four — e.g. 128 (common in Hadamard/Quarot configs), 8, or 0; typically from a hand-edited builder config.","commonSituations":"Porting a SpinQuant/Quarot recipe that assumes powers of two; sharing a quantization config across models with a group size tuned for a different rotation scheme; typo in the builder's group parameter.","solutions":["Set the group to 4, 16, or 64 in the builder config","Validate the config at startup with is_power_of_four(group_size) so builders fail fast before downloading/loading a checkpoint","Keep the group out of per-request paths; it is a build-time constant recorded into checkpoint metadata"],"exampleFix":"# before\napply_rotation(transformer, fqns, group_size=128)\n\n# after\napply_rotation(transformer, fqns, group_size=64)","handlingStrategy":"validation","validationCode":"from studio.backend.core.inference.diffusion_convrot import is_power_of_four\n\nassert is_power_of_four(group_size), f\"ConvRot group {group_size} must be 4, 16, or 64\"","typeGuard":null,"tryCatchPattern":"try:\n    rotated = apply_rotation(transformer, fqns, group_size=group_size)\nexcept ValueError as e:\n    raise BuildConfigError(str(e)) from e  # stop the build; never silently rotate fewer layers","preventionTips":["Check is_power_of_four at config parse time so a bad group fails in seconds, not after a checkpoint download","Port Hadamard configs from other schemes by remapping group 128 -> 64 (or 16)","Unit-test the builder config against a tiny model in CI"],"tags":["convrot","config-mistake","quantization","offline-build"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}