{"record":{"id":"e3bfc1f4f4c1708f","repo":"sgl-project/sglang","slug":"inklingmultimodalprocessor-v1-requires-pre-rendere","errorCode":null,"errorMessage":"InklingMultimodalProcessor v1 requires pre-rendered input_ids (request_obj.input_ids); the custom Inkling chat renderer is a separate workstream. No tokenizer available to render text.","messagePattern":"InklingMultimodalProcessor v1 requires pre-rendered input_ids \\(request_obj\\.input_ids\\); the custom Inkling chat renderer is a separate workstream\\. No tokenizer available to render text\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/processors/inkling.py","lineNumber":302,"sourceCode":"            audio_token_id=self.AUDIO_TOKEN_ID,\n            audio_end_id=self.AUDIO_END_TOKEN_ID,\n        )\n\n    # ---- SGLang entrypoint ----------------------------------------------\n\n    async def process_mm_data_async(\n        self,\n        image_data: Optional[List[Union[str, bytes, Dict]]] = None,\n        audio_data: Optional[List[Union[str, bytes, Dict]]] = None,\n        input_text: str = \"\",\n        request_obj: Any = None,\n        *args,\n        **kwargs,\n    ) -> Optional[MultimodalProcessorOutput]:\n        input_ids = getattr(request_obj, \"input_ids\", None)\n        if input_ids is None:\n            if self._tokenizer is None:\n                raise ValueError(\n                    \"InklingMultimodalProcessor v1 requires pre-rendered input_ids \"\n                    \"(request_obj.input_ids); the custom Inkling chat renderer is a \"\n                    \"separate workstream. No tokenizer available to render text.\"\n                )\n            input_ids = self._tokenizer(input_text).input_ids\n        if isinstance(input_ids, torch.Tensor):\n            input_ids = input_ids.flatten().tolist()\n\n        # Resolve request media (data:/http URLs, ImageData objects) to bytes so the\n        # Inkling preprocessors can consume them; bytes / paths pass through unchanged.\n        if image_data:\n            image_data = [_resolve_media_item(it) for it in image_data]\n        if audio_data:\n            audio_data = [_resolve_media_item(it) for it in audio_data]\n        return self.assemble(list(input_ids), image_data, audio_data)\n","sourceCodeStart":284,"sourceCodeEnd":318,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/processors/inkling.py#L284-L318","documentation":"Inkling's v1 processor cannot render raw text prompts: it needs pre-rendered input_ids on the request, and if input_ids is missing it only falls back to self._tokenizer when one is available. When no tokenizer is present, it raises to avoid silently mis-rendering prompts with the custom Inkling chat format.","triggerScenarios":"Calling process_mm_data_async with a text-only request_obj (input_ids=None) in a deployment path where the processor was constructed without a tokenizer; sending {'text': ...} instead of {'input_ids': [...]}.","commonSituations":"Embedding/offline pipelines that instantiate the processor standalone without the tokenizer manager; engine configs that skip tokenizer init for pre-tokenized-only workloads but then receive text requests.","solutions":["Pre-render input_ids with the Inkling chat renderer and pass them via request_obj.input_ids","Ensure the processor is constructed with the tokenizer so the text fallback works","Route text-only requests through the tokenizer manager instead of the raw processor API"],"exampleFix":"# before\nout = await processor.process_mm_data_async(None, {'text': prompt}, request_obj)\n# after\nrequest_obj.input_ids = tokenizer(prompt)['input_ids']\nout = await processor.process_mm_data_async(None, {'input_ids': request_obj.input_ids}, request_obj)","handlingStrategy":"validation","validationCode":"if getattr(request_obj, 'input_ids', None) is None and processor has no tokenizer:\n    request_obj.input_ids = render_inkling_template(msgs)  # must pre-render","typeGuard":"def request_has_prerendered_ids(req) -> bool:\n    return getattr(req, 'input_ids', None) is not None","tryCatchPattern":"try:\n    out = await processor.process_mm_data_async(None, mm_data, req)\nexcept ValueError as e:\n    if 'pre-rendered input_ids' in str(e):\n        req.input_ids = tokenizer(req.input_text)['input_ids']\n        out = await processor.process_mm_data_async(None, mm_data, req)\n    else: raise","preventionTips":["Construct the Inkling processor with the tokenizer in offline tools","Standardize on input_ids-carrying request objects for this model"],"tags":["multimodal","inkling","pre-tokenized-input","missing-tokenizer"],"backgroundTag":"missing-prerendered-input-ids","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}