{"record":{"id":"a3c938ef29cce371","repo":"apache/flink","slug":"buffer-underflow","errorCode":null,"errorMessage":"Buffer underflow.","messagePattern":"Buffer underflow\\.","errorType":"exception","errorClass":"KryoBufferUnderflowException","httpStatus":null,"severity":"error","filePath":"flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/NoFetchingInput.java","lineNumber":83,"sourceCode":"        // The main change between this and Kryo 5 Input.require is this will never read more bytes\n        // than required.\n        // There are also formatting changes to be compliant with the Flink project styling rules.\n        int remaining = limit - position;\n        if (remaining >= required) {\n            return remaining;\n        }\n        if (required > capacity) {\n            throw new KryoException(\n                    \"Buffer too small: capacity: \" + capacity + \", required: \" + required);\n        }\n\n        int count;\n        // Try to fill the buffer.\n        if (remaining > 0) {\n            // Logical change 1 (from Kryo Input.require): \"capacity - limit\" -> \"required - limit\"\n            count = fill(buffer, limit, required - limit);\n            if (count == -1) {\n                throw new KryoBufferUnderflowException(\"Buffer underflow.\");\n            }\n            remaining += count;\n            if (remaining >= required) {\n                limit += count;\n                return remaining;\n            }\n        }\n\n        // Was not enough, compact and try again.\n        System.arraycopy(buffer, position, buffer, 0, remaining);\n        total += position;\n        position = 0;\n\n        do {\n            // Logical change 2 (from Kryo Input.require): \"capacity - remaining\" -> \"required -\n            // remaining\"\n            count = fill(buffer, remaining, required - remaining);\n            if (count == -1) {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/apache/flink/blob/2f3c205e9266cb30240eb7f4fdab15cad629a70f/flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/NoFetchingInput.java#L65-L101","documentation":"Thrown inside NoFetchingInput.require() when the underlying InputStream hits EOF (fill returns -1) while trying to top up the buffer to satisfy a read of 'required' bytes. It means the serialized stream ended before a complete value was available.","triggerScenarios":"Reading a Kryo-serialized record through NoFetchingInput when the DataInputView/source has fewer bytes remaining than the value being read needs - e.g. reading past the end of a serialized record boundary, or a truncated stream.","commonSituations":"Truncated checkpoint/snapshot files; length-prefixed records where the length is wrong (corruption or writer bug); deserializing a stream with a different format than expected; network stream cut mid-record.","solutions":["Verify the byte stream is complete and was produced by the matching serializer","Check length prefixes / record framing written upstream for off-by-one or truncation bugs","If restoring state, confirm the checkpoint file is fully persisted and not partially uploaded"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    T v = serializer.deserialize(inputView);\n} catch (KryoException e) {\n    if (e.getCause() instanceof EOFException || e.getMessage().contains(\"underflow\")) {\n        // truncated stream: fail the partition/read, do not retry blindly\n        throw new IOException(\"Truncated serialized stream\", e);\n    }\n    throw e;\n}","preventionTips":["Ensure streams are fully written and flushed before deserialization","Verify framing/length prefixes on both writer and reader","Do not reuse buffers across concurrent readers"],"tags":["kryo","serialization","eof","stream-corruption"],"backgroundTag":null,"analyzedSha":"2f3c205e9266cb30240eb7f4fdab15cad629a70f","analyzedAt":"2026-08-14T08:48:24.518Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}