{"record":{"id":"57d8323c50f495e8","repo":"sgl-project/sglang","slug":"text-must-be-str-got-type-text-name","errorCode":null,"errorMessage":"text must be str, got {type(text).__name__}","messagePattern":"text must be str, got (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/parser/inkling_tokenizer.py","lineNumber":96,"sourceCode":"        return INKLING_SPECIAL_TOKEN_NAMES[token]\n    except KeyError as exc:\n        raise KeyError(f\"unknown Inkling special token: {token!r}\") from exc\n\n\n@dataclass(frozen=True)\nclass InklingTokenizer:\n    \"\"\"Small wrapper around a base text tokenizer plus Inkling framing IDs.\n\n    Plain text is encoded by the base tokenizer, while the minimal chat\n    framing tokens are inserted from the fixed overlay map.\n    \"\"\"\n\n    tokenizer: Any\n    special_token_ids: Mapping[str, int] | None = None\n\n    def encode_text(self, text: str) -> list[int]:\n        if not isinstance(text, str):\n            raise TypeError(f\"text must be str, got {type(text).__name__}\")\n        return list(self.tokenizer.encode(text, add_special_tokens=False))\n\n    def encode_special(self, token: str) -> int:\n        special = normalize_special_token(token)\n        token_ids = self.special_token_ids or INKLING_SPECIAL_TOKEN_IDS\n        return int(token_ids[special])\n\n    def decode(self, token_ids: list[int]) -> str:\n        return self.tokenizer.decode(token_ids)\n","sourceCodeStart":78,"sourceCodeEnd":106,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/parser/inkling_tokenizer.py#L78-L106","documentation":"encode_text strictly requires a str and raises TypeError naming the actual type otherwise. The Inkling tokenizer wrapper does no implicit coercion before delegating to the base tokenizer's encode().","triggerScenarios":"Calling encode_text with bytes, None, or an int — e.g. encode_text(b'hello') or encode_text(None) after a content field was left unset.","commonSituations":"Content fields that are None when a message has only image parts, bytes read from files/streams, or integers from token IDs mistakenly passed back as text.","solutions":["Coerce or guard before calling: decode bytes, substitute '' for None","Check upstream why the value isn't str — usually an empty multimodal content list","Add an isinstance check in your message-assembly code"],"exampleFix":"// before\nids = tok.encode_text(content)  # content may be None\n// after\nids = tok.encode_text(content if isinstance(content, str) else \"\")","handlingStrategy":"type-guard","validationCode":"text = content if isinstance(content, str) else (content.decode(\"utf-8\") if isinstance(content, bytes) else \"\")\nids = tok.encode_text(text)","typeGuard":"def is_str_text(v: Any) -> TypeGuard[str]:\n    return isinstance(v, str)","tryCatchPattern":"try:\n    ids = tok.encode_text(text)\nexcept TypeError as e:\n    raise ValueError(f\"non-text content reached tokenizer: {e}\") from e","preventionTips":["Normalize message content fields to str at ingestion","Treat None content as empty string explicitly","Never pass bytes or token IDs back into encode_text"],"tags":["inkling","tokenizer","type-error","encode"],"backgroundTag":"wrong-argument-type","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}