{"record":{"id":"e79a737935c4ada5","repo":"unslothai/unsloth","slug":"extra-llama-server-args-cannot-contain-control-cha","errorCode":null,"errorMessage":"extra llama-server args cannot contain control characters","messagePattern":"extra llama-server args cannot contain control characters","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/llama_server_args.py","lineNumber":271,"sourceCode":"        # is on the whole list rather than per token.\n        # Strictly, unlike the sizing below: JSON and the browser can both carry an\n        # unpaired surrogate, which survives every check here and then makes\n        # subprocess.Popen raise while it encodes argv, long after the load has begun\n        # switching models. Refused at the boundary, where it is still a 400.\n        try:\n            encoded = token.encode(\"utf-8\")\n        except UnicodeEncodeError as error:\n            raise ValueError(\n                \"extra llama-server args cannot contain unpaired surrogate characters\"\n            ) from error\n        total_bytes += len(encoded)\n        limit = max_extra_args_bytes()\n        if total_bytes > limit:\n            raise ValueError(f\"extra llama-server args are too large (limit {limit} bytes)\")\n        # execve rejects a NUL outright; the rest would reach the child's parser as\n        # invisible characters and be blamed on the flag they are attached to.\n        if _has_control_characters(token):\n            raise ValueError(\"extra llama-server args cannot contain control characters\")\n        flag = _flag_name(token)\n        if flag is not None and flag in _DENYLIST:\n            raise ValueError(\n                f\"llama-server flag '{flag}' is managed by Unsloth Studio \"\n                f\"and cannot be passed as an extra arg\"\n            )\n        if flag is None:\n            # A token belonging to no flag. Today's llama-server answers \"invalid\n            # argument\" and refuses to start, which is a failed load rather than a\n            # 400, and a build that did accept a positional would read it as the\n            # model path: that is the one thing the -m / --model denial exists to\n            # prevent, and it would sidestep the native-path lease as well.\n            if pending_values <= 0:\n                raise ValueError(\n                    \"extra llama-server args cannot contain a bare value \"\n                    f\"('{token[:64]}'); every value must follow its flag\"\n                )\n            pending_values -= 1","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/llama_server_args.py#L253-L289","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"# before\nraw = \"--chat-template\\\\\\n  {{...}}\"  # backslash-newline continuation survives\nargs = raw.split()\n\n# after\nargs = [t for line in raw.splitlines() for t in line.split()]  # clean flatten\n# or: args = [strip_controls(t) for t in args]","handlingStrategy":"validation","validationCode":"def has_control_chars(s) -> bool:\n    return any(unicodedata.category(c) == 'Cc' for c in s)\n# reject before submit:\nassert not any(has_control_chars(t) for t in extra_args)","typeGuard":"def is_control_free(s) -> bool:\n    return all(unicodedata.category(c) != 'Cc' for c in s)","tryCatchPattern":"try:\n    validate_extra_args(args)\nexcept ValueError as e:\n    if \"control characters\" in str(e):\n        args = [''.join(c for c in t if ord(c) >= 32) for t in args]\n        validate_extra_args(args)\n    else:\n        raise","preventionTips":["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."],"tags":["llama-server","validation","control-characters","argv","shell"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}