{"record":{"id":"8d7dbec8b5d1d65a","repo":"unslothai/unsloth","slug":"cannot-rotate-fqn-r-not-an-nn-linear-on-this-mo","errorCode":null,"errorMessage":"cannot rotate {fqn!r}: not an nn.Linear on this model","messagePattern":"cannot rotate (.+?): not an nn\\.Linear on this model","errorType":"exception","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/diffusion_convrot.py","lineNumber":315,"sourceCode":"    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, ...]:\n    \"\"\"ONLINE half: install the input rotation on exactly the fqns ``metadata`` records.\n\n    Returns the fqns rotated, or ``()`` when ``metadata`` declares no rotation -- the plain\n    artifacts, which have to be left exactly as they are. RAISES on any other outcome: an\n    unusable contract, an fqn this model does not have, a target that is not a Linear, an\n    ``in_features`` the recorded group does not divide, or a Linear already rotated. The prequant","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/diffusion_convrot.py#L297-L333","documentation":"During offline weight rotation, one of the requested fully-qualified module names resolved to something that is not an nn.Linear (or to nothing the isinstance check accepts). Only plain Linears can be block-rotated, so the builder refuses rather than rotating a wrong module type.","triggerScenarios":"Passing an fqn naming a Conv1d, Embedding, LayerNorm, or a wrapper/override of Linear that fails isinstance(module, nn.Linear); also an fqn that resolves to None is caught by the same check. Happens when the fqn list was written for a different architecture revision.","commonSituations":"Model architecture updated and a target layer became a fused/quantized wrapper or was renamed; fqn list generated by an older naming pass (e.g. missing or extra '.'); targeting attention projections that this revision implements as a single bundled module.","solutions":["Print dict(transformer.named_modules()) and correct the fqn to name an actual nn.Linear","Regenerate the fqn list from the live model (filter isinstance(module, nn.Linear)) instead of hardcoding it","If the layer genuinely changed type in this architecture, remove it from the rotation set"],"exampleFix":"# before\napply_rotation(transformer, [\"model.layers.0.mlp\"], 64)  # module is the MLP block\n\n# after\napply_rotation(transformer, [\"model.layers.0.mlp.down_proj\"], 64)  # an nn.Linear","handlingStrategy":"type-guard","validationCode":"from torch import nn\n\ndef linear_fqns(transformer) -> list[str]:\n    return [fqn for fqn, m in transformer.named_modules() if isinstance(m, nn.Linear)]","typeGuard":"from torch import nn\n\ndef is_plain_linear(module) -> bool:\n    \"\"\"True only for modules the offline rotation accepts.\"\"\"\n    return isinstance(module, nn.Linear)","tryCatchPattern":"try:\n    apply_rotation(transformer, fqns, group_size)\nexcept ValueError as e:\n    raise BuildError(str(e)) from e  # regenerate the fqn list from this model, do not edit by hand","preventionTips":["Generate fqn lists programmatically from named_modules() on the exact model being rotated","Re-generate the list whenever the model architecture revision changes","Prefer raising over partial rotation: the API already returns only the fqns it rotated"],"tags":["convrot","fqn","model-mismatch","quantization"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}