{"record":{"id":"a794b1df2f180504","repo":"karpathy/nanochat","slug":"unknown-content-type-type-content","errorCode":null,"errorMessage":"Unknown content type: {type(content)}","messagePattern":"Unknown content type: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"nanochat/tokenizer.py","lineNumber":218,"sourceCode":"                        value_ids = self.encode(part[\"text\"])\n                        if part[\"type\"] == \"text\":\n                            # string part => simply add the tokens\n                            add_tokens(value_ids, 1)\n                        elif part[\"type\"] == \"python\":\n                            # python tool call => add the tokens inside <|python_start|> and <|python_end|>\n                            add_tokens(python_start, 1)\n                            add_tokens(value_ids, 1)\n                            add_tokens(python_end, 1)\n                        elif part[\"type\"] == \"python_output\":\n                            # python output => add the tokens inside <|output_start|> and <|output_end|>\n                            # none of these tokens are supervised because the tokens come from Python at test time\n                            add_tokens(output_start, 0)\n                            add_tokens(value_ids, 0)\n                            add_tokens(output_end, 0)\n                        else:\n                            raise ValueError(f\"Unknown part type: {part['type']}\")\n                else:\n                    raise ValueError(f\"Unknown content type: {type(content)}\")\n                add_tokens(assistant_end, 1)\n\n        # truncate to max_tokens tokens MAX (helps prevent OOMs)\n        ids = ids[:max_tokens]\n        mask = mask[:max_tokens]\n        return ids, mask\n\n    def visualize_tokenization(self, ids, mask, with_token_id=False):\n        \"\"\"Small helper function useful in debugging: visualize the tokenization of render_conversation\"\"\"\n        RED = '\\033[91m'\n        GREEN = '\\033[92m'\n        RESET = '\\033[0m'\n        GRAY = '\\033[90m'\n        tokens = []\n        for i, (token_id, mask_val) in enumerate(zip(ids, mask)):\n            token_str = self.decode([token_id])\n            color = GREEN if mask_val == 1 else RED\n            tokens.append(f\"{color}{token_str}{RESET}\")","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/karpathy/nanochat/blob/92d63d4e8bb4df75c3b71618f31ddde2378b2bcd/nanochat/tokenizer.py#L200-L236","documentation":"In `render_conversation`, an assistant message's `content` must be either a plain str or a list of part dicts. Any other type (int, None, dict, bytes) hits the else on the assistant branch and raises ValueError naming the type. (User messages have a separate strict isinstance(content, str) assert.)","triggerScenarios":"A conversation where an assistant message's content is None (common in OpenAI-format data when tool_calls are used and content is nulled), a single dict instead of a list of dicts, or a number/other non-string scalar.","commonSituations":"Loading OpenAI-format chat logs where assistant tool-call messages have \"content\": null; a dataset bug where content fields were dropped; nested dict-of-parts instead of list-of-parts.","solutions":["Normalize the dataset: replace null assistant content with [] (empty part list) or a placeholder 'text' part.","Ensure assistant content is str or a list of {type, text} dicts.","Add a schema check over conversations before passing them to the tokenizer (see validation code)."],"exampleFix":"# before\n{\"role\": \"assistant\", \"content\": None, \"tool_calls\": [...]}\n\n# after\n{\"role\": \"assistant\", \"content\": [{\"type\": \"python\", \"text\": \"calc(2+2)\"}]}","handlingStrategy":"type-guard","validationCode":"for i, msg in enumerate(conversation[\"messages\"]):\n    content = msg.get(\"content\")\n    if msg[\"role\"] == \"user\":\n        assert isinstance(content, str), \"user content must be str\"\n    else:\n        assert isinstance(content, (str, list)), f\"assistant content must be str or list of parts, got {type(content).__name__}\"","typeGuard":"def is_valid_assistant_content(content) -> bool:\n    return isinstance(content, str) or (isinstance(content, list) and all(isinstance(p, dict) for p in content))","tryCatchPattern":null,"preventionTips":["Normalize null assistant content (common in OpenAI-format tool-call messages) to [] or a text part.","Ensure the system message (if present) is followed by a user message — render_conversation also asserts on that.","Run a dataset-wide schema lint before SFT training."],"tags":["nanochat","tokenizer","sft-data","type-error","validation"],"backgroundTag":null,"analyzedSha":"92d63d4e8bb4df75c3b71618f31ddde2378b2bcd","analyzedAt":"2026-08-15T03:11:54.371Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}