fishaudio/fish-speech · error · ValueError

Either text or tokens must be provided

Error message

Either text or tokens must be provided

What it means

TextPart is a dataclass part of a ContentSequence that can represent content either as raw text or as pre-tokenized token IDs. In __post_init__, if both the text field and tokens field are left as None, the part has no content to encode, so a ValueError is raised. This protects downstream tokenization/encoding from receiving an empty part.

Source

Thrown at fish_speech/content_sequence.py:49

class VQPart(BasePart):
    type = "vq"
    codes: torch.Tensor

    def __post_init__(self: "VQPart"):
        self.type = "vq"
        self.codes = restore_ndarray(self.codes, to_tensor=True)


@dataclass(kw_only=True)
class TextPart(BasePart):
    type = "text"
    text: str | None = None
    tokens: list[int] | None = None

    def __post_init__(self: "TextPart"):
        self.type = "text"
        if self.text is None and self.tokens is None:
            raise ValueError("Either text or tokens must be provided")


@dataclass(kw_only=True)
class AudioPart(BasePart):
    type = "audio"
    features: torch.Tensor

    def __post_init__(self: "AudioPart"):
        self.type = "audio"
        self.features = restore_ndarray(self.features, to_tensor=True)


@dataclass(kw_only=True)
class EncodedMessage:
    tokens: torch.Tensor
    labels: torch.Tensor
    vq_mask_tokens: torch.Tensor | None = None
    vq_mask_labels: torch.Tensor | None = None

View on GitHub (pinned to befe400174)

Solutions

  1. Pass text="..." or tokens=[...] when creating TextPart; exactly one is required
  2. If generating parts dynamically, guard with `if not text and not tokens: continue` before appending the part
  3. Check for accidental None from upstream data sources (e.g. missing keys in JSON input) that feed into TextPart

Example fix

// before
part = TextPart()  # ValueError: Either text or tokens must be provided

// after
part = TextPart(text="hello")
# or
part = TextPart(tokens=[1, 2, 3])
Defensive patterns

Strategy: validation

Validate before calling

def make_text_part(text=None, tokens=None):
    if text is None and tokens is None:
        raise ValueError("skip: no content")
    return TextPart(text=text, tokens=tokens)

Type guard

from fish_speech.content_sequence import TextPart

def is_valid_text_part(d: dict) -> bool:
    return d.get("type") == "text" and (d.get("text") is not None or d.get("tokens") is not None)

Try / catch

try:
    part = TextPart(**part_dict)
except ValueError as e:
    logger.warning("skipping malformed text part: %s", e)
    continue

Prevention

When it happens

Trigger: Constructing TextPart() or TextPart(text=None, tokens=None) without passing either field; or building a dict for ContentSequence(parts=[{"type": "text"}]) where neither key is set.

Common situations: Programmatically building parts lists where a text part dict is created but the text value was accidentally None (e.g. empty string from an upstream API became None), or copy-pasting a TextPart construction and deleting the payload.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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