unslothai/unsloth · error · ValueError

extra llama-server args cannot contain unpaired surrogate ch

Error message

extra llama-server args cannot contain unpaired surrogate characters

What it means

ValueError from the per-token walk in llama_server_args.py:261 — a token in the extra args fails token.encode('utf-8') because it contains an unpaired surrogate (e.g. a lone '\ud83d' from bad JSON or browser input). Such a string survives Python and JSON checks but crashes subprocess.Popen when it encodes argv, long after the model switch has begun. The check refuses it at the boundary, where the client still gets a 400.

Source

Thrown at studio/backend/core/inference/llama_server_args.py:261

    # arity this module knows for certain.
    pending_two_value = 0
    two_value_flag = ""
    for raw in args:
        token = str(raw)
        if len(out) >= MAX_EXTRA_ARG_TOKENS:
            raise ValueError(
                f"too many extra llama-server args (limit {MAX_EXTRA_ARG_TOKENS} tokens)"
            )
        # A grammar or JSON schema is a legitimately long single token, so the cap
        # is on the whole list rather than per token.
        # Strictly, unlike the sizing below: JSON and the browser can both carry an
        # unpaired surrogate, which survives every check here and then makes
        # subprocess.Popen raise while it encodes argv, long after the load has begun
        # switching models. Refused at the boundary, where it is still a 400.
        try:
            encoded = token.encode("utf-8")
        except UnicodeEncodeError as error:
            raise ValueError(
                "extra llama-server args cannot contain unpaired surrogate characters"
            ) from error
        total_bytes += len(encoded)
        limit = max_extra_args_bytes()
        if total_bytes > limit:
            raise ValueError(f"extra llama-server args are too large (limit {limit} bytes)")
        # execve rejects a NUL outright; the rest would reach the child's parser as
        # invisible characters and be blamed on the flag they are attached to.
        if _has_control_characters(token):
            raise ValueError("extra llama-server args cannot contain control characters")
        flag = _flag_name(token)
        if flag is not None and flag in _DENYLIST:
            raise ValueError(
                f"llama-server flag '{flag}' is managed by Unsloth Studio "
                f"and cannot be passed as an extra arg"
            )
        if flag is None:
            # A token belonging to no flag. Today's llama-server answers "invalid

View on GitHub (pinned to 203007d190)

Solutions

  1. Fix the producer: encode/decode strings as proper UTF-8 end-to-end; in JS, operate on code points (Array.from) not UTF-16 code units before slicing.
  2. Sanitize before submit: token.encode('utf-8', 'strict') client-side, or re-encode via token.encode('utf-8','surrogatepass').decode('utf-8','replace') to drop orphans.
  3. If the arg legitimately contains the mangled text, replace the emoji/surrogate with a normal character and resubmit.
  4. Locate the surrogate: print [hex(ord(c)) for c in token if 0xD800 <= ord(c) <= 0xDFFF].

Example fix

# before
args = json.loads(raw_body)["extra_args"]  # may carry lone surrogates
validate_extra_args(args)  # ValueError

# after
def sanitize(tokens):
    return [t.encode("utf-8", "replace").decode("utf-8") for t in tokens]
validate_extra_args(sanitize(args))
Defensive patterns

Strategy: validation

Validate before calling

def utf8_safe(tokens):
    for t in tokens:
        t.encode("utf-8")  # raises on lone surrogates
    return tokens

Type guard

def is_utf8_encodable(s) -> bool:
    try:
        s.encode("utf-8")
        return True
    except UnicodeEncodeError:
        return False

Try / catch

try:
    validate_extra_args(args)
except ValueError as e:
    if "surrogate" in str(e):
        args = [t.encode("utf-8", "replace").decode("utf-8") for t in args]
        validate_extra_args(args)  # retry once with sanitized args
    else:
        raise

Prevention

When it happens

Trigger: Submitting extra args containing a lone surrogate half — typically from decoding JSON with surrogateescape, from JavaScript string manipulation that split an emoji, or from pasting text mangled by a terminal/encoding conversion.

Common situations: Frontend splits/slices a UTF-16 string mid-emoji and sends the orphaned half; Python reads config with errors='surrogateescape'; a chat-template override pasted from a broken copy contains U+D800-U+DFFF alone.

Related errors


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