invoke-ai/InvokeAI · error · ValueError

`class_embed_type`: 'projection' requires `projection_class_

Error message

`class_embed_type`: 'projection' requires `projection_class_embeddings_input_dim` be set

What it means

When `class_embed_type='projection'`, the class-label embedding is a linear projection whose input size must be known, so `projection_class_embeddings_input_dim` is mandatory. The constructor raises this ValueError when the projection type is selected but the input dimension is missing, because TimestepEmbedding cannot be sized without it. Related: when this projection type is used, `addition_embed_type` must typically also be None, and adding_time_dims derive from this value.

Source

Thrown at invokeai/backend/util/hotfixes.py:238

            )

        elif encoder_hid_dim_type is not None:
            raise ValueError(
                f"encoder_hid_dim_type: {encoder_hid_dim_type} must be None, 'text_proj' or 'text_image_proj'."
            )
        else:
            self.encoder_hid_proj = None

        # class embedding
        if class_embed_type is None and num_class_embeds is not None:
            self.class_embedding = nn.Embedding(num_class_embeds, time_embed_dim)
        elif class_embed_type == "timestep":
            self.class_embedding = TimestepEmbedding(timestep_input_dim, time_embed_dim)
        elif class_embed_type == "identity":
            self.class_embedding = nn.Identity(time_embed_dim, time_embed_dim)
        elif class_embed_type == "projection":
            if projection_class_embeddings_input_dim is None:
                raise ValueError(
                    "`class_embed_type`: 'projection' requires `projection_class_embeddings_input_dim` be set"
                )
            # The projection `class_embed_type` is the same as the timestep `class_embed_type` except
            # 1. the `class_labels` inputs are not first converted to sinusoidal embeddings
            # 2. it projects from an arbitrary input dimension.
            #
            # Note that `TimestepEmbedding` is quite general, being mainly linear layers and activations.
            # When used for embedding actual timesteps, the timesteps are first converted to sinusoidal embeddings.
            # As a result, `TimestepEmbedding` can be passed arbitrary vectors.
            self.class_embedding = TimestepEmbedding(projection_class_embeddings_input_dim, time_embed_dim)
        else:
            self.class_embedding = None

        if addition_embed_type == "text":
            if encoder_hid_dim is not None:
                text_time_embedding_from_dim = encoder_hid_dim
            else:
                text_time_embedding_from_dim = cross_attention_dim

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Set `projection_class_embeddings_input_dim` to the summed size of all conditioning embeddings (e.g. SDXL: 2816 = 4*281 timesteps + 768 text + 1280+... per model card).
  2. If you don't need projection class embedding, change `class_embed_type` to None, 'timestep', or 'identity'.
  3. Load with the original, complete config.json via from_pretrained instead of reconstructing kwargs manually.
  4. Diff your constructor kwargs against the upstream reference config for the checkpoint you are loading.

Example fix

// before
model = ControlNetModel2_5(
    class_embed_type="projection",
    addition_embed_type="text_time",
)
// after
model = ControlNetModel2_5(
    class_embed_type="projection",
    addition_embed_type="text_time",
    projection_class_embeddings_input_dim=2816,
)
Defensive patterns

Strategy: validation

Validate before calling

if model_config.get("class_embed_type") == "projection" and model_config.get("projection_class_embeddings_input_dim") is None:
    raise ValueError("class_embed_type='projection' needs projection_class_embeddings_input_dim")

Type guard

def projection_config_complete(cfg: dict) -> bool:
    if cfg.get("class_embed_type") != "projection":
        return True
    return isinstance(cfg.get("projection_class_embeddings_input_dim"), int)

Try / catch

try:
    model = ControlNetModel2_5(**cfg)
except ValueError as e:
    if "projection_class_embeddings_input_dim" in str(e):
        cfg["projection_class_embeddings_input_dim"] = 2816  # SDXL default total cond dim
        model = ControlNetModel2_5(**cfg)
    else:
        raise

Prevention

When it happens

Trigger: Constructing the model with `class_embed_type="projection"` while `projection_class_embeddings_input_dim` is None/omitted — e.g. a partially copied SDXL-style config where the projection keys were dropped, or a config.json that sets class_embed_type without the accompanying dimension key.

Common situations: Adapting an SDXL/SSD-1B config onto a ControlNet/UNet class; truncating a config dict when copying only some keys; checkpoint configs authored for models that defaulted projection_class_embeddings_input_dim at a higher level (pipeline/scheduler config) instead of the model config.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/788a195b6f6f1900. Report an issue: GitHub.