unslothai/unsloth · error · ValueError
extra llama-server args cannot contain control characters
Error message
extra llama-server args cannot contain control characters
What it means
ValueError from llama_server_args.py:271 — a token in the extra args contains control characters (via _has_control_characters). execve rejects NUL outright, and other invisible characters would pass through to llama-server's parser and be blamed on whatever flag they are attached to, producing confusing 'invalid argument' errors at spawn time. Refused early as a 400.
Source
Thrown at studio/backend/core/inference/llama_server_args.py:271
# 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
# argument" and refuses to start, which is a failed load rather than a
# 400, and a build that did accept a positional would read it as the
# model path: that is the one thing the -m / --model denial exists to
# prevent, and it would sidestep the native-path lease as well.
if pending_values <= 0:
raise ValueError(
"extra llama-server args cannot contain a bare value "
f"('{token[:64]}'); every value must follow its flag"
)
pending_values -= 1View on GitHub (pinned to 203007d190)
Solutions
- Flatten multi-line shell commands into one line before submitting: remove backslash-newline continuations.
- Strip control characters from each token before submit: ''.join(c for c in t if unicodedata.category(c) != 'Cc').
- If a value legitimately needs newlines (e.g. a chat template), pass it via a file path instead of inline argv.
- Inspect for hidden characters: repr(token) or [hex(ord(c)) for c in token] to find the offending byte.
Example fix
# before
raw = "--chat-template\\\n {{...}}" # backslash-newline continuation survives
args = raw.split()
# after
args = [t for line in raw.splitlines() for t in line.split()] # clean flatten
# or: args = [strip_controls(t) for t in args] Defensive patterns
Strategy: validation
Validate before calling
def has_control_chars(s) -> bool:
return any(unicodedata.category(c) == 'Cc' for c in s)
# reject before submit:
assert not any(has_control_chars(t) for t in extra_args) Type guard
def is_control_free(s) -> bool:
return all(unicodedata.category(c) != 'Cc' for c in s) Try / catch
try:
validate_extra_args(args)
except ValueError as e:
if "control characters" in str(e):
args = [''.join(c for c in t if ord(c) >= 32) for t in args]
validate_extra_args(args)
else:
raise Prevention
- Flatten multi-line shell snippets (remove backslash-newline continuations) before splitting.
- Strip \r from configs parsed on Windows (CRLF line endings).
- Use repr(token) to spot invisible characters while debugging.
- Keep newline-bearing values (chat templates) in files, not argv.
When it happens
Trigger: Extra args containing \x00, \r, \n, \t, or other C0/C1 control codes — usually from copy-pasting a multi-line shell command where line continuations or newlines survive into a single token, or from programmatic arg building that includes raw control bytes.
Common situations: Pasting a shell snippet with backslash-newline continuations into the extra-args UI; a config file with Windows CRLF line endings parsed so \r rides along on the last token of each line; a chat template containing literal \n inside a token (values are also checked).
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 a bare value ('{token
- llama-server does not accept the spaces around '{token[:64]}
- llama-server does not read an attached value: write '{flag}'
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/e79a737935c4ada5.
Report an issue: GitHub.