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 "invalidView on GitHub (pinned to 203007d190)
Solutions
- 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.
- 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.
- If the arg legitimately contains the mangled text, replace the emoji/surrogate with a normal character and resubmit.
- 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
- Never decode external text with errors='surrogateescape' for values that become argv.
- In JS, slice by code points (Array.from(str)) so emojis are never split into surrogate halves.
- Run a strict UTF-8 round-trip check on all user-supplied config strings at ingest.
- Replace or drop orphan surrogates before persistence, not at launch time.
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
- too many extra llama-server args (limit {MAX_EXTRA_ARG_TOKEN
- extra llama-server args are too large (limit {limit} bytes)
- extra llama-server args cannot contain control characters
- llama-server flag '{flag}' is managed by Unsloth Studio and
- extra llama-server args cannot contain a bare value ('{token
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/b78a499b488d5dd8.
Report an issue: GitHub.