{"record":{"id":"135577670d2a0846","repo":"apache/iceberg","slug":"cannot-write-block-exceeded-integer-max-value-blo","errorCode":null,"errorMessage":"Cannot write block: exceeded Integer.MAX_VALUE blocks","messagePattern":"Cannot write block: exceeded Integer\\.MAX_VALUE blocks","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/encryption/AesGcmOutputStream.java","lineNumber":147,"sourceCode":"    targetStream.close();\n  }\n\n  @Override\n  public long storedLength() throws IOException {\n    return targetStream.storedLength();\n  }\n\n  private void writeHeader() throws IOException {\n    targetStream.write(HEADER_BYTES);\n    isHeaderWritten = true;\n  }\n\n  private void encryptAndWriteBlock() throws IOException {\n    Preconditions.checkState(\n        !lastBlockWritten, \"Cannot encrypt block: a partial block has already been written\");\n\n    if (currentBlockIndex == Integer.MAX_VALUE) {\n      throw new IOException(\"Cannot write block: exceeded Integer.MAX_VALUE blocks\");\n    }\n\n    if (positionInPlainBlock == 0 && currentBlockIndex != 0) {\n      return;\n    }\n\n    if (positionInPlainBlock != plainBlock.length) {\n      // signal that a partial block has been written and must be the last\n      this.lastBlockWritten = true;\n    }\n\n    byte[] aad = Ciphers.streamBlockAAD(fileAadPrefix, currentBlockIndex);\n    int ciphertextLength =\n        gcmEncryptor.encrypt(plainBlock, 0, positionInPlainBlock, cipherBlock, 0, aad);\n    targetStream.write(cipherBlock, 0, ciphertextLength);\n    positionInPlainBlock = 0;\n    currentBlockIndex++;\n  }","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/encryption/AesGcmOutputStream.java#L129-L165","documentation":"The GCM stream format numbers blocks with a 32-bit counter (the block index is encoded into the AAD via a little-endian int). When currentBlockIndex reaches Integer.MAX_VALUE no further block can be keyed, so writing the next block throws IOException to prevent silent nonce/AAD reuse, which would be a cryptographic failure.","triggerScenarios":"Writing more than Integer.MAX_VALUE blocks (~2 billion 4MiB default blocks, far less with tiny block sizes) into a single AesGcmOutputStream before close; flushBlock boundaries hit at index Integer.MAX_VALUE.","commonSituations":"Very large single encrypted file writes; a misconfigured tiny block size (write.block-size property) causing block count to explode long before the byte limit matters.","solutions":["Increase the block size so the total block count stays far below Integer.MAX_VALUE","Split the data across multiple files/streams instead of one stream","Check configuration for an accidentally small encryption block size"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before writing a huge stream\nlong maxBytes = (long) Integer.MAX_VALUE * blockSizeBytes;\nif (expectedTotalBytes > maxBytes) {\n  throw new IllegalArgumentException(\"Split data across files; exceeds max GCM blocks\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  out.write(chunk);\n} catch (IOException e) {\n  if (e.getMessage().contains(\"exceeded Integer.MAX_VALUE blocks\")) {\n    throw new IllegalStateException(\"File too large for single GCM stream; reduce block-size or split file\", e);\n  } else { throw e; }\n}","preventionTips":["Keep encryption block size at its default; don't shrink it for large files","Estimate block count = totalBytes / blockSize before writing very large files","Split multi-terabyte datasets across multiple encrypted files"],"tags":["encryption","limit-exceeded","stream"],"backgroundTag":"value-out-of-range","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}