{"record":{"id":"695be0d45dee3ad1","repo":"apache/iceberg","slug":"not-enough-header-bytes","errorCode":null,"errorMessage":"Not enough header bytes","messagePattern":"Not enough header bytes","errorType":"exception","errorClass":"BadHeaderException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/data/avro/IcebergDecoder.java","lineNumber":132,"sourceCode":"    }\n\n    if (resolver != null) {\n      Schema writeSchema = resolver.findByFingerprint(fp);\n      if (writeSchema != null) {\n        addSchema(writeSchema);\n        return decoders.get(fp);\n      }\n    }\n\n    throw new MissingSchemaException(\"Cannot resolve schema for fingerprint: \" + fp);\n  }\n\n  @Override\n  public D decode(InputStream stream, D reuse) throws IOException {\n    byte[] header = HEADER_BUFFER.get();\n    try {\n      if (!readFully(stream, header)) {\n        throw new BadHeaderException(\"Not enough header bytes\");\n      }\n    } catch (IOException e) {\n      throw new IOException(\"Failed to read header and fingerprint bytes\", e);\n    }\n\n    if (IcebergEncoder.V1_HEADER[0] != header[0] || IcebergEncoder.V1_HEADER[1] != header[1]) {\n      throw new BadHeaderException(\n          String.format(\n              Locale.ROOT, \"Unrecognized header bytes: 0x%02X 0x%02X\", header[0], header[1]));\n    }\n\n    RawDecoder<D> decoder = getDecoder(FP_BUFFER.get().getLong(2));\n\n    try {\n      return decoder.decode(stream, reuse);\n    } catch (UncheckedIOException e) {\n      throw new AvroRuntimeException(e);\n    }","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/data/avro/IcebergDecoder.java#L114-L150","documentation":"IcebergDecoder.decode reads a fixed header (magic bytes plus fingerprint) before dispatching to the per-fingerprint decoder. If the stream ends before the full header (HEADER bytes plus fingerprint) can be read, it throws BadHeaderException(\"Not enough header bytes\"). This usually means the stream is empty or truncated.","triggerScenarios":"Calling decode(stream, reuse) with an empty InputStream, or a stream shorter than the header+fingerprint size (e.g. a zero-byte file or a stream already fully consumed).","commonSituations":"Empty output files from failed writes; re-reading a stream after it was drained; passing wrong stream (e.g. an empty in-memory BAOS) to the decoder.","solutions":["Check the stream is non-empty and correctly positioned before decoding (stream.available() > 0 or file length check).","Verify the file/stream actually contains Iceberg-encoded records (writer completed and flushed).","Reset/rewind the InputStream if it was previously read.","Handle BadHeaderException at the call site to treat empty input as an empty batch."],"exampleFix":"// before\ndecoder.decode(new FileInputStream(emptyFile), null); // BadHeaderException\n// after\nFile f = new File(path);\nif (f.length() > 0) {\n  decoder.decode(new FileInputStream(f), null);\n}","handlingStrategy":"validation","validationCode":"if (file.length() == 0) return; // or treat as empty batch\ncount bytes available before decode","typeGuard":"null","tryCatchPattern":"try { return decoder.decode(stream, reuse); } catch (BadHeaderException e) { LOG.warn(\"Empty or truncated stream\"); return null; }","preventionTips":["Check file/stream length before decoding","Rewind streams before reuse","Verify writers flush and close output before consumers read"],"tags":["avro","decoder","stream","truncated-data"],"backgroundTag":"file-read-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}