fishaudio/fish-speech · error · ValueError

Unsupported part type

Error message

Unsupported part type: {type(part)}

What it means

ContentSequence.encode walks over self.parts and encodes each part (text parts are tokenized, VQ parts produce semantic tokens). If a part is neither a TextPart, VQPart, AudioPart, nor other handled isinstance branch, encode has no encoding strategy and raises ValueError naming the offending type.

Solutions

  1. Ensure all parts are instances of the supported dataclasses (TextPart/VQPart/AudioPart) before calling encode
  2. Construct the sequence through ContentSequence(...) so dicts are normalized in __init__
  3. If you defined a custom part type, extend/patch encode() to handle it via isinstance dispatch

Example fix

# before
seq.parts.append({"type": "text", "text": "hi"})
seq.encode(...)

# after
seq.parts.append(TextPart(text="hi"))
seq.encode(...)
Defensive patterns

Strategy: type-guard

Validate before calling

from fish_speech.content_sequence import TextPart, VQPart, AudioPart
SUPPORTED = (TextPart, VQPart, AudioPart)
assert all(isinstance(p, SUPPORTED) for p in seq.parts), "unencoded part present"

Type guard

from fish_speech.content_sequence import TextPart, VQPart, AudioPart

def is_encodable(part) -> bool:
    return isinstance(part, (TextPart, VQPart, AudioPart))

Try / catch

try:
    encoded = seq.encode(...)
except ValueError as e:
    if "Unsupported part type" in str(e):
        bad = [p for p in seq.parts if not isinstance(p, (TextPart, VQPart, AudioPart))]
        raise RuntimeError(f"bad parts: {bad}") from e
    raise

Prevention

When it happens

Trigger: Calling ContentSequence.encode() (directly or via encode_for_inference/visualize) when parts contains a raw dict that wasn't normalized (constructed by bypassing __init__ fixes), a None, or a custom/BasePart subclass that encode() doesn't handle.

Common situations: Appending parts to an existing sequence's .parts list directly, subclassing BasePart without extending encode(), or storing plain dicts in parts after object creation.

Related errors


AI-assisted analysis of fishaudio/fish-speech@befe400174 (2026-08-27). Data as JSON: /api/errors/5592bf276e61e78c. Report an issue: GitHub.

Appendix: source

Thrown at fish_speech/content_sequence.py:214

                    tokens = tokenizer.encode(part.text, add_special_tokens=False)
                else:
                    tokens = part.tokens

                tokens = torch.tensor(tokens, dtype=torch.long)
            elif isinstance(part, VQPart):
                # Critical Optimization: Vectorized mapping
                # Instead of loop lookup: [tokenizer.semantic_id_to_token_id[i] for i in codes]
                # We use arithmetic offset: code + semantic_begin_id
                # This assumes semantic tokens are contiguous in the vocab (DualAR requirement)
                curr_codes = part.codes.clone().to(torch.int)

                # Use int64 (long) for token IDs to avoid overflow or type mismatch in embedding
                tokens = (curr_codes[0] + tokenizer.semantic_begin_id).to(torch.long)

                vq_parts.append(curr_codes)
                vq_require_losses.append(part.cal_loss)
            else:
                raise ValueError(f"Unsupported part type: {type(part)}")

            all_tokens.append(tokens)

            # Set masks for different part types
            if isinstance(part, VQPart):
                vq_masks.append(torch.ones_like(tokens, dtype=torch.bool))
                audio_masks.append(torch.zeros_like(tokens, dtype=torch.bool))
            elif isinstance(part, AudioPart):
                vq_masks.append(torch.zeros_like(tokens, dtype=torch.bool))
                audio_mask = torch.ones_like(tokens, dtype=torch.bool)
                audio_mask[0] = False  # Skip start token
                audio_mask[-1] = False  # Skip end token
                audio_masks.append(audio_mask)
            else:
                vq_masks.append(torch.zeros_like(tokens, dtype=torch.bool))
                audio_masks.append(torch.zeros_like(tokens, dtype=torch.bool))

            # Set labels based on whether we want to calculate loss for this part

View on GitHub (pinned to befe400174)