{"record":{"id":"6075f16d8abd8c16","repo":"sgl-project/sglang","slug":"nvimagecodec-returned-an-invalid-jpeg-tensor-shap","errorCode":null,"errorMessage":"nvImageCodec returned an invalid JPEG tensor: shape={tuple(image.shape)}, dtype={image.dtype}","messagePattern":"nvImageCodec returned an invalid JPEG tensor: shape=(.+?), dtype=(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/utils/nvjpeg_decoder.py","lineNumber":82,"sourceCode":"\n\n@lru_cache(maxsize=None)\ndef _get_decoder_pool(device_id: int) -> _NvJpegDecoderPool:\n    return _NvJpegDecoderPool(device_id)\n\n\ndef decode_jpeg_with_fancy_upsampling(image_bytes: bytes) -> torch.Tensor:\n    \"\"\"Decode a JPEG to contiguous CHW RGB uint8 on the current CUDA device.\n\n    torchvision's CUDA JPEG decoder creates nvJPEG with its default flags,\n    which use nearest-neighbor chroma upsampling. nvImageCodec exposes nvJPEG's\n    interpolated (\"fancy\") upsampling and exports the result to PyTorch through\n    DLPack without copying it.\n    \"\"\"\n    device_id = torch.cuda.current_device()\n    image = _get_decoder_pool(device_id).decode(image_bytes)\n    if image.ndim != 3 or image.shape[0] != 3 or image.dtype != torch.uint8:\n        raise RuntimeError(\n            \"nvImageCodec returned an invalid JPEG tensor: \"\n            f\"shape={tuple(image.shape)}, dtype={image.dtype}\"\n        )\n    return image\n","sourceCodeStart":64,"sourceCodeEnd":87,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/utils/nvjpeg_decoder.py#L64-L87","documentation":"decode_jpeg_with_fancy_upsampling got a tensor back from nvImageCodec but it is not a 3D uint8 CHW tensor with 3 channels. The guard checks ndim==3, shape[0]==3, dtype==uint8, so grayscale (1 channel), RGBA (4 channels), or unexpected layouts/dtypes trigger this RuntimeError.","triggerScenarios":"Decoding a grayscale JPEG (nvImageCodec yields 1-channel), an image decoded to a non-standard layout, or a colorspace config mismatch in the decode params producing non-uint8 output.","commonSituations":"Datasets mixing RGB and grayscale JPEGs; images with unusual color spaces; version changes in nvImageCodec altering output layout.","solutions":["Convert grayscale inputs to RGB before submission (PIL convert('RGB')) or pre-check the JPEG component count","Add a try/except with CPU fallback that normalizes to RGB","Pin/verify the nvImageCodec version and decode params (output color format) used by the pool"],"exampleFix":"# before\nimg = decode_jpeg_with_fancy_upsampling(data)  # grayscale JPEG -> RuntimeError\n# after\ntry:\n    img = decode_jpeg_with_fancy_upsampling(data)\nexcept RuntimeError:\n    img = cpu_decode_rgb(data)","handlingStrategy":"try-catch","validationCode":"def jpeg_channel_count(path) -> int:\n    from PIL import Image\n    with Image.open(path) as im:\n        return len(im.getbands())","typeGuard":"null","tryCatchPattern":"try:\n    img = decode_jpeg_with_fancy_upsampling(data)\nexcept RuntimeError as e:\n    if 'invalid JPEG tensor' in str(e):\n        img = decode_with_pil(data).convert('RGB')\n    else:\n        raise","preventionTips":["Convert grayscale/CMYK images to RGB in preprocessing","Pin nvImageCodec version in deployments","Add dataset scans for 1-/4-channel JPEGs before GPU serving"],"tags":["nvjpeg","image-decoding","tensor-shape","multimodal","sglang"],"backgroundTag":"image-decode-failed","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}