invoke-ai/InvokeAI · error · ValueError
Got {params.axes_dim} but expected positional dim {pe_dim}
Error message
Got {params.axes_dim} but expected positional dim {pe_dim} What it means
InstantXControlNetFlux.__init__ computes the positional-embedding dimension as hidden_size // num_heads and requires the sum of params.axes_dim (the RoPE per-axis dims) to equal that value. If the FluxParams config supplies an axes_dim list whose elements don't sum to pe_dim, the model would be built with an inconsistent rotary embedding, so it refuses to construct. This is a config-integrity check mirroring the upstream flux reference model.
Source
Thrown at invokeai/backend/flux/controlnet/instantx_controlnet_flux.py:59
def __init__(self, params: FluxParams, num_control_modes: int | None = None):
"""
Args:
params (FluxParams): The parameters for the FLUX model.
num_control_modes (int | None, optional): The number of controlnet modes. If non-None, then the model is a
'union controlnet' model and expects a mode conditioning input at runtime.
"""
super().__init__()
# The following modules mirror the base FLUX transformer model.
# -------------------------------------------------------------
self.params = params
self.in_channels = params.in_channels
self.out_channels = self.in_channels
if params.hidden_size % params.num_heads != 0:
raise ValueError(f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}")
pe_dim = params.hidden_size // params.num_heads
if sum(params.axes_dim) != pe_dim:
raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
self.hidden_size = params.hidden_size
self.num_heads = params.num_heads
self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
self.img_in = nn.Linear(self.in_channels, self.hidden_size, bias=True)
self.time_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size)
self.vector_in = MLPEmbedder(params.vec_in_dim, self.hidden_size)
self.guidance_in = (
MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size) if params.guidance_embed else nn.Identity()
)
self.txt_in = nn.Linear(params.context_in_dim, self.hidden_size)
self.double_blocks = nn.ModuleList(
[
DoubleStreamBlock(
self.hidden_size,
self.num_heads,
mlp_ratio=params.mlp_ratio,
qkv_bias=params.qkv_bias,View on GitHub (pinned to 0b6a024f2f)
Solutions
- Set params.axes_dim so its elements sum to params.hidden_size // params.num_heads (default Flux: hidden_size=3072, num_heads=24 → pe_dim=128, axes_dim=[16,56,56]).
- Adjust num_heads to a divisor of hidden_size that makes hidden_size/num_heads equal your axes_dim sum.
- Restore the default FluxParams values matching the checkpoint you loaded.
- Verify the checkpoint's config JSON (hidden_size, num_heads, axes_dim) matches what you pass in.
Example fix
// before params = FluxParams(in_channels=64, hidden_size=2048, num_heads=24, axes_dim=[16,56,56], ...) # sum=128 != 2048/24 // after params = FluxParams(in_channels=64, hidden_size=3072, num_heads=24, axes_dim=[16,56,56], ...) # 3072/24=128 == sum(axes_dim)
Defensive patterns
Strategy: validation
Validate before calling
pe_dim = params.hidden_size // params.num_heads
assert params.hidden_size % params.num_heads == 0, "hidden_size must be divisible by num_heads"
assert sum(params.axes_dim) == pe_dim, f"sum(axes_dim)={sum(params.axes_dim)} != pe_dim={pe_dim}" Type guard
def is_valid_flux_params(p) -> bool:
return p.hidden_size % p.num_heads == 0 and sum(p.axes_dim) == p.hidden_size // p.num_heads Try / catch
try:
controlnet = InstantXControlNetFlux(params=params)
except ValueError as e:
if "expected positional dim" in str(e):
pe_dim = params.hidden_size // params.num_heads
params = replace(params, axes_dim=scale_axes_dim(params.axes_dim, pe_dim))
controlnet = InstantXControlNetFlux(params=params)
else:
raise Prevention
- Keep FluxParams sourced from the checkpoint's config file, never hand-typed.
- Add an assertion that sum(axes_dim) == hidden_size // num_heads in your config loader.
- Remember the canonical values: hidden_size=3072, num_heads=24, axes_dim=[16,56,56].
When it happens
Trigger: Constructing InstantXControlNetFlux(params=FluxParams(...)) where sum(params.axes_dim) != params.hidden_size // params.num_heads — e.g. custom hidden_size/num_heads copied from another checkpoint while keeping default axes_dim=[16,56,56] (sums to 128).
Common situations: Adapting the ControlNet to a non-standard Flux variant or a distilled model with different head counts; hand-editing FluxParams; porting configs between Flux schnell/dev and XLabs/InstantX checkpoints where pe layouts differ.
Related errors
- Input img and txt tensors must have 3 dimensions.
- Hidden size {params.hidden_size} must be divisible by num_he
- Got {params.axes_dim} but expected positional dim {pe_dim}
- The Anima ControlNet-LLLite model '{lllite_field.control_mod
- This Anima ControlNet-LLLite adapter is an inpainting adapte
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/8a95a6a6f3488fdb.
Report an issue: GitHub.