{"record":{"id":"45f122e81a2e8b1b","repo":"docling-project/docling","slug":"invalid-bytes-data-insufficient-bytes-for-string","errorCode":null,"errorMessage":"Invalid BYTES data: insufficient bytes for string of length {str_len} at offset {offset}","messagePattern":"Invalid BYTES data: insufficient bytes for string of length (.+?) at offset (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"docling/models/inference_engines/common/kserve_v2_utils.py","lineNumber":42,"sourceCode":"    for value in tensor.reshape(-1):\n        encoded = encode_bytes_element(value)\n        chunks.append(len(encoded).to_bytes(4, byteorder=\"little\"))\n        chunks.append(encoded)\n    return b\"\".join(chunks)\n\n\ndef decode_bytes_tensor(raw_output: bytes, shape: tuple[int, ...]) -> np.ndarray:\n    \"\"\"Decode a length-prefixed BYTES payload to a numpy object array.\"\"\"\n    strings, offset = [], 0\n    for _ in range(int(np.prod(shape))):\n        if offset + 4 > len(raw_output):\n            raise RuntimeError(\n                f\"Invalid BYTES data: insufficient bytes for length prefix at offset {offset}\"\n            )\n        str_len = int.from_bytes(raw_output[offset : offset + 4], byteorder=\"little\")\n        offset += 4\n        if offset + str_len > len(raw_output):\n            raise RuntimeError(\n                f\"Invalid BYTES data: insufficient bytes for string of length {str_len} at offset {offset}\"\n            )\n        strings.append(raw_output[offset : offset + str_len])\n        offset += str_len\n    return np.array(strings, dtype=object).reshape(shape)\n","sourceCodeStart":24,"sourceCodeEnd":48,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/models/inference_engines/common/kserve_v2_utils.py#L24-L48","documentation":"Raised while decoding a KServe v2 BYTES-tensor response payload. The decoder reads a 4-byte little-endian length prefix per string, then expects that many bytes to follow; if the remaining buffer is shorter than the declared string length, the payload is truncated or malformed. This indicates the remote server returned a corrupted or non-conforming BYTES tensor, or the response shape does not match a length-prefixed BYTES encoding.","triggerScenarios":"Calling an inference endpoint whose output datatype is BYTES (via KserveV2Client.infer and decode_bytes_tensor) where the raw output bytes are shorter than sum(4 + str_len) implied by the tensor shape. Happens with shape/byte-count mismatch, partial gRPC/HTTP response, or a server that serializes BYTES without the KServe length-prefix convention.","commonSituations":"Deploying a custom KServe v2 model that returns strings in a non-standard encoding; mismatch between declared output shape and actual payload; network proxies truncating responses; server version change altering the serialization format.","solutions":["Verify the server actually returns KServe v2 conforming BYTES tensors (each element prefixed by a 4-byte little-endian length); test with a known-good KServe example model.","Check that the output tensor shape declared in model metadata matches the number of length-prefixed strings actually serialized in the payload.","Capture raw_output bytes and len(raw_output) at the failure offset to confirm whether the payload is truncated (transport issue) vs. mis-encoded (server issue).","If the server cannot be fixed, avoid the BYTES path and request a numeric (FP32/INT64) output instead, decoding it locally."],"exampleFix":"// before: server packs raw concatenated strings\nbuf = b''.join(strings)  # no length prefixes\n\n// after: KServe v2 conforming BYTES encoding\nimport struct\nbuf = b''.join(struct.pack('<I', len(s)) + s for s in strings)","handlingStrategy":"validation","validationCode":"def validate_bytes_payload(raw: bytes, shape: tuple[int, ...]) -> None:\n    offset, count = 0, int(np.prod(shape))\n    for i in range(count):\n        if offset + 4 > len(raw):\n            raise ValueError(f\"truncated length prefix at element {i}\")\n        n = int.from_bytes(raw[offset:offset + 4], \"little\")\n        offset += 4\n        if offset + n > len(raw):\n            raise ValueError(f\"truncated string at element {i} (need {n} bytes)\")\n        offset += n","typeGuard":null,"tryCatchPattern":"try:\n    arr = decode_bytes_tensor(raw_output, shape)\nexcept RuntimeError as e:\n    if \"Invalid BYTES data\" in str(e):\n        log.error(\"server returned malformed BYTES tensor: %s\", e)\n        raise  # server-side defect; retrying unchanged payload won't help","preventionTips":["Smoke-test the server with a single tiny request and validate the BYTES payload before running production batches.","Pin the server version so serialization behavior cannot silently change.","Prefer numeric (FP32) outputs over BYTES where the model allows it."],"tags":["kserve","serialization","bytes","tensor-decoding","remote-inference"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}