unslothai/unsloth · error · ValueError

No bicodec_semantic tokens found in generated output

Error message

No bicodec_semantic tokens found in generated output

What it means

Raised during BiCodec (e.g. MeiGate/E2-style TTS) decode when the regex for <|bicodec_semantic_N|> tokens finds zero matches in the generated text. The LLM was supposed to emit semantic tokens as inline special-token text but produced none — the generation ended early, used the wrong template, or the tokenizer does not mark these ids as special so they never render in the text.

Source

Thrown at studio/backend/core/inference/audio_codecs.py:209

        return _numpy_to_wav_bytes(waveform, 24000), 24000

    def decode_bicodec(self, generated_text: str, device: str) -> Tuple[bytes, int]:
        """Decode BiCodec tokens (Spark-TTS) from generated text.

        Extracts bicodec_semantic_N and bicodec_global_N tokens via regex.
        Returns (wav_bytes, sample_rate).
        """
        semantic_matches = re.findall(r"<\|bicodec_semantic_(\d+)\|>", generated_text)
        global_matches = re.findall(r"<\|bicodec_global_(\d+)\|>", generated_text)

        logger.info(
            f"BiCodec decode: {len(global_matches)} global tokens, {len(semantic_matches)} semantic tokens"
        )
        if len(global_matches) < 10:
            logger.info(f"BiCodec generated text (first 500 chars): {generated_text[:500]}")

        if not semantic_matches:
            raise ValueError("No bicodec_semantic tokens found in generated output")

        semantic_ids = torch.tensor([int(t) for t in semantic_matches]).long().unsqueeze(0)

        # Speaker encoder expects exactly 32 global tokens (token_num=32);
        # pad with zeros or truncate.
        GLOBAL_TOKEN_NUM = 32
        if global_matches:
            raw = [int(t) for t in global_matches]
        else:
            raw = []
        if len(raw) < GLOBAL_TOKEN_NUM:
            raw = raw + [0] * (GLOBAL_TOKEN_NUM - len(raw))
        raw = raw[:GLOBAL_TOKEN_NUM]
        global_ids = torch.tensor(raw).long().unsqueeze(0)  # (1, 32)

        self._bicodec_tokenizer.device = device
        self._bicodec_tokenizer.model.to(device)

View on GitHub (pinned to 203007d190)

Solutions

  1. Inspect the logged 'BiCodec generated text (first 500 chars)' to see what the model actually produced — prose instead of tokens means a prompt/template problem
  2. Ensure the model's tokenizer_config registers the bicodec_semantic/bicodec_global tokens as special tokens
  3. Adjust sampling (temperature, min_p, repetition penalty) and retry — early-EOS is often stochastic
  4. Confirm the loaded checkpoint is the audio-capable variant and the generation prompt includes the required prefix
Defensive patterns

Strategy: fallback

Validate before calling

import re
def has_bicodec_output(text: str) -> bool:
    return bool(re.search(r"<\|bicodec_semantic_\d+\|>", text))

Try / catch

try:
    wav, sr = codec.decode_bicodec(generated_text, device)
except ValueError as e:
    if "bicodec_semantic" in str(e):
        log_first_500(generated_text)  # inspect what the model emitted
        regenerate()

Prevention

When it happens

Trigger: Generation stopped before emitting any semantic tokens (EOS-first); chat template missing the TTS prompt structure so the model answers in plain prose; tokenizer's added-special-token set lacks the bicodec tokens so decode() renders them as raw text or nothing; wrong model loaded for the requested decode path.

Common situations: Using a GGUF/quantized variant whose special tokens are not registered; prompt/prefill not ending with the audio-generation start marker; sampling params causing immediate EOS; version drift between tokenizer config and model card.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/432ccfb28802654a. Report an issue: GitHub.