{"record":{"id":"5592bf276e61e78c","repo":"fishaudio/fish-speech","slug":"unsupported-part-type-type-part","errorCode":null,"errorMessage":"Unsupported part type: {type(part)}","messagePattern":"Unsupported part type: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fish_speech/content_sequence.py","lineNumber":214,"sourceCode":"                    tokens = tokenizer.encode(part.text, add_special_tokens=False)\n                else:\n                    tokens = part.tokens\n\n                tokens = torch.tensor(tokens, dtype=torch.long)\n            elif isinstance(part, VQPart):\n                # Critical Optimization: Vectorized mapping\n                # Instead of loop lookup: [tokenizer.semantic_id_to_token_id[i] for i in codes]\n                # We use arithmetic offset: code + semantic_begin_id\n                # This assumes semantic tokens are contiguous in the vocab (DualAR requirement)\n                curr_codes = part.codes.clone().to(torch.int)\n\n                # Use int64 (long) for token IDs to avoid overflow or type mismatch in embedding\n                tokens = (curr_codes[0] + tokenizer.semantic_begin_id).to(torch.long)\n\n                vq_parts.append(curr_codes)\n                vq_require_losses.append(part.cal_loss)\n            else:\n                raise ValueError(f\"Unsupported part type: {type(part)}\")\n\n            all_tokens.append(tokens)\n\n            # Set masks for different part types\n            if isinstance(part, VQPart):\n                vq_masks.append(torch.ones_like(tokens, dtype=torch.bool))\n                audio_masks.append(torch.zeros_like(tokens, dtype=torch.bool))\n            elif isinstance(part, AudioPart):\n                vq_masks.append(torch.zeros_like(tokens, dtype=torch.bool))\n                audio_mask = torch.ones_like(tokens, dtype=torch.bool)\n                audio_mask[0] = False  # Skip start token\n                audio_mask[-1] = False  # Skip end token\n                audio_masks.append(audio_mask)\n            else:\n                vq_masks.append(torch.zeros_like(tokens, dtype=torch.bool))\n                audio_masks.append(torch.zeros_like(tokens, dtype=torch.bool))\n\n            # Set labels based on whether we want to calculate loss for this part","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/fishaudio/fish-speech/blob/befe4001745417f8c42131739d862b8a6fdbd15a/fish_speech/content_sequence.py#L196-L232","documentation":"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.","triggerScenarios":"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.","commonSituations":"Appending parts to an existing sequence's .parts list directly, subclassing BasePart without extending encode(), or storing plain dicts in parts after object creation.","solutions":["Ensure all parts are instances of the supported dataclasses (TextPart/VQPart/AudioPart) before calling encode","Construct the sequence through ContentSequence(...) so dicts are normalized in __init__","If you defined a custom part type, extend/patch encode() to handle it via isinstance dispatch"],"exampleFix":"# before\nseq.parts.append({\"type\": \"text\", \"text\": \"hi\"})\nseq.encode(...)\n\n# after\nseq.parts.append(TextPart(text=\"hi\"))\nseq.encode(...)","handlingStrategy":"type-guard","validationCode":"from fish_speech.content_sequence import TextPart, VQPart, AudioPart\nSUPPORTED = (TextPart, VQPart, AudioPart)\nassert all(isinstance(p, SUPPORTED) for p in seq.parts), \"unencoded part present\"","typeGuard":"from fish_speech.content_sequence import TextPart, VQPart, AudioPart\n\ndef is_encodable(part) -> bool:\n    return isinstance(part, (TextPart, VQPart, AudioPart))","tryCatchPattern":"try:\n    encoded = seq.encode(...)\nexcept ValueError as e:\n    if \"Unsupported part type\" in str(e):\n        bad = [p for p in seq.parts if not isinstance(p, (TextPart, VQPart, AudioPart))]\n        raise RuntimeError(f\"bad parts: {bad}\") from e\n    raise","preventionTips":["Only append typed part objects to .parts; never raw dicts","If subclassing parts, also extend encode()'s dispatch"],"tags":["validation","encoding","content-sequence","python"],"backgroundTag":"unsupported-type-dispatch","analyzedSha":"befe4001745417f8c42131739d862b8a6fdbd15a","analyzedAt":"2026-08-27T21:31:45.703Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}