fishaudio/fish-speech · error · ValueError

Reference ID contains invalid characters or is too long. Onl

Error message

Reference ID contains invalid characters or is too long. Only alphanumeric, hyphens, underscores, and spaces are allowed.

What it means

ReferenceLoader validates user-supplied reference IDs against a regex (alphanumerics, hyphens, underscores, spaces) plus a 255-char length limit before using the ID as a directory name. An invalid ID raises ValueError, both as input validation and as path-traversal protection (blocking ../ and special characters).

Source

Thrown at fish_speech/inference_engine/reference_loader.py:57

            backends = torchaudio.list_audio_backends()
            if "ffmpeg" in backends:
                self.backend = "ffmpeg"
            else:
                self.backend = "soundfile"
        except AttributeError:
            # torchaudio 2.9+ removed list_audio_backends()
            # Try ffmpeg first, fallback to soundfile
            try:
                __import__("torchaudio.io._load_audio_fileobj")

                self.backend = "ffmpeg"
            except (ImportError, ModuleNotFoundError):
                self.backend = "soundfile"

    @staticmethod
    def _validate_id(id: str) -> None:
        if not _ID_PATTERN.match(id) or len(id) > 255:
            raise ValueError(
                "Reference ID contains invalid characters or is too long. "
                "Only alphanumeric, hyphens, underscores, and spaces are allowed."
            )

    def load_by_id(
        self,
        id: str,
        use_cache: Literal["on", "off"],
    ) -> Tuple:
        self._validate_id(id)

        # Load the references audio and text by id
        ref_folder = Path("references") / id
        ref_folder.mkdir(parents=True, exist_ok=True)
        ref_audios = list_files(
            ref_folder, AUDIO_EXTENSIONS, recursive=True, sort=False
        )

View on GitHub (pinned to befe400174)

Solutions

  1. Sanitize the ID: keep [A-Za-z0-9_- ] only and trim it
  2. If the ID comes from user input, validate/normalize it before calling the API
  3. For long IDs, use a hash or short slug instead

Example fix

# before
loader.add_reference("../evil", "ref.wav")  # ValueError

# after
import re
safe = re.sub(r"[^\w\- ]", "_", user_id)[:255]
loader.add_reference(safe, "ref.wav")
Defensive patterns

Strategy: validation

Validate before calling

import re
_ID_RE = re.compile(r"^[A-Za-z0-9_-]+( [A-Za-z0-9_-]+)*$")

def safe_id(s: str) -> str:
    s = re.sub(r"[^\w\- ]", "_", s).strip()
    if not _ID_RE.match(s) or len(s) > 255:
        raise ValueError("bad id")
    return s

Type guard

import re

def is_valid_reference_id(id: str) -> bool:
    return bool(re.match(r"^[A-Za-z0-9_-]+( [A-Za-z0-9_-]+)*$", id)) and len(id) <= 255

Try / catch

try:
    loader.load_by_id(ref_id)
except ValueError as e:
    if "invalid characters" in str(e):
        ref_id = re.sub(r"[^\w\- ]", "_", ref_id)[:255]
        loader.load_by_id(ref_id)
    else:
        raise

Prevention

When it happens

Trigger: Calling load_by_id, add_reference, or delete_reference with an ID containing slashes, dots, unicode, or exceeding 255 characters — e.g. "my/ref" or "spéaker!".

Common situations: Using usernames, filenames, or free-form user input directly as reference IDs; IDs containing path separators cause this immediately as a security guard.

Understand the failure class

Related errors


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