apache/hadoop · error · IOException

%s closed

Error message

%s closed

What it means

Native raw decoders (ISA-L backed, e.g. NativeRSRawDecoder) hold a long nativeCoder handle to the C-side coder; close()/release() frees it and leaves the field 0. A subsequent doDecode detects nativeCoder == 0 and throws IOException("<ClassName> closed") — a use-after-close on native resources that can no longer serve requests.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/erasurecode/rawcoder/AbstractNativeRawDecoder.java:53

  public static Logger LOG =
      LoggerFactory.getLogger(AbstractNativeRawDecoder.class);

  // Protect ISA-L coder data structure in native layer from being accessed and
  // updated concurrently by the init, release and decode functions.
  protected final ReentrantReadWriteLock decoderLock =
      new ReentrantReadWriteLock();

  public AbstractNativeRawDecoder(ErasureCoderOptions coderOptions) {
    super(coderOptions);
  }

  @Override
  protected void doDecode(ByteBufferDecodingState decodingState)
      throws IOException {
    decoderLock.readLock().lock();
    try {
      if (nativeCoder == 0) {
        throw new IOException(String.format("%s closed",
            getClass().getSimpleName()));
      }
      int[] inputOffsets = new int[decodingState.inputs.length];
      int[] outputOffsets = new int[decodingState.outputs.length];

      ByteBuffer buffer;
      for (int i = 0; i < decodingState.inputs.length; ++i) {
        buffer = decodingState.inputs[i];
        if (buffer != null) {
          inputOffsets[i] = buffer.position();
        }
      }

      for (int i = 0; i < decodingState.outputs.length; ++i) {
        buffer = decodingState.outputs[i];
        outputOffsets[i] = buffer.position();
      }

View on GitHub (pinned to 2add963021)

Solutions

  1. Do not reuse a native raw decoder after close(); create a fresh one from the RawErasureCoderFactory (e.g., via CodecUtil.createRawDecoder) for the next workload
  2. Scope try-with-resources around each unit of work, or manage a pool that invalidates closed instances and never returns them
  3. Audit error paths that close coders so they also evict the instance from any cache/pool

Example fix

// before
RawErasureDecoder decoder = CodecUtil.createRawDecoder(conf, "rs", opts);
try (RawErasureDecoder d = decoder) {
  d.decode(inputs, erased, outputs);
} // closes the shared instance
decoder.decode(inputs2, erased2, outputs2); // IOException: ... closed

// after
RawErasureDecoder decoder = CodecUtil.createRawDecoder(conf, "rs", opts);
try {
  decoder.decode(inputs2, erased2, outputs2);
} finally {
  decoder.close();
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Track lifecycle yourself: create per workload or pool with invalidation
RawErasureDecoder decoder = CodecUtil.createRawDecoder(conf, "rs", opts);
// use decoder ... then decoder.close() exactly once at end of workload

Try / catch

try {
  decoder.decode(inputs, erased, outputs);
} catch (IOException e) {
  if (e.getMessage().endsWith("closed")) {
    decoder = CodecUtil.createRawDecoder(conf, "rs", opts); // fresh handle
    decoder.decode(inputs, erased, outputs); // retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Calling decode() on a native RawErasureDecoder after close() was invoked (manually, by another thread, or by try-with-resources scoping); pooling native decoders and closing one on an error path while the pool still hands it out.

Common situations: Wrapping a long-lived/pooled coder in try-with-resources so it closes after the first operation; cleanup code that closes shared coders; double close followed by reuse.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/e02e5fbae11375bb. Report an issue: GitHub.