apache/hadoop · error · IOException

Null IO stream from reopen of ({}) {}

Error message

Null IO stream from reopen of ({}) {}

What it means

OBSInputStream.reopen (the lazy-seek/reader that issues a ranged GetObject) requires the OBS SDK to return object content; if client.getObject(request).getObjectContent() yields null, the connector throws IOException('Null IO stream from reopen of (reason) uri'). A null body after a successful call indicates a broken/edge-case SDK response rather than a normal missing object (that path raises ObsException and is translated). It is effectively a defensive assertion that the reopened HTTP stream is usable.

Source

Thrown at hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSInputStream.java:236

    if (wrappedStream != null) {
      closeStream("reopen(" + reason + ")", contentRangeFinish);
    }

    contentRangeFinish =
        calculateRequestLimit(targetPos, length, contentLength,
            readAheadRange);

    try {
      GetObjectRequest request = new GetObjectRequest(bucket, key);
      request.setRangeStart(targetPos);
      request.setRangeEnd(contentRangeFinish);
      if (fs.getSse().isSseCEnable()) {
        request.setSseCHeader(fs.getSse().getSseCHeader());
      }
      wrappedStream = client.getObject(request).getObjectContent();
      contentRangeStart = targetPos;
      if (wrappedStream == null) {
        throw new IOException(
            "Null IO stream from reopen of (" + reason + ") " + uri);
      }
    } catch (ObsException e) {
      throw translateException("Reopen at position " + targetPos, uri, e);
    }

    this.streamCurrentPos = targetPos;
    long endTime = System.currentTimeMillis();
    LOG.debug(
        "reopen({}) for {} range[{}-{}], length={},"
            + " streamPosition={}, nextReadPosition={}, thread={}, "
            + "timeUsedInMilliSec={}",
        uri,
        reason,
        targetPos,
        contentRangeFinish,
        length,
        streamCurrentPos,

View on GitHub (pinned to 2add963021)

Solutions

  1. Align the OBS SDK version with the connector's required version (check the module's pom dependencies) and remove duplicate SDK jars from the classpath
  2. Retry the read operation: this is not deterministic state corruption — the next reopen usually succeeds; wrap positional reads in a bounded retry
  3. If behind a proxy/gateway, inspect whether ranged GET responses are being altered (e.g. 200-without-body instead of 206)
  4. In tests, ensure the mocked client returns non-null content for every ranged GetObject request

Example fix

// before
int n = in.read(buf, off, len); // positional read -> reopen -> null content -> IOException

// after
int n = retryingRead(in, buf, off, len, 3);

// helper
int retryingRead(FSDataInputStream in, byte[] b, int off, int len, int attempts) throws IOException {
  IOException last = null;
  for (int i = 0; i < attempts; i++) {
    try {
      return in.read(b, off, len);
    } catch (IOException e) {
      if (!(e.getMessage() != null && e.getMessage().contains("Null IO stream"))) throw e;
      last = e;
    }
  }
  throw last;
}
Defensive patterns

Strategy: retry

Try / catch

try {
  return in.read(buf, off, len);
} catch (IOException e) {
  if (String.valueOf(e.getMessage()).contains("Null IO stream from reopen")) {
    // anomalous SDK response: safe to retry the same positional read once or twice
    return retryPositionalRead(in, buf, off, len, 2);
  }
  throw e;
}

Prevention

When it happens

Trigger: Random-access reads (seek + read, positional read) that trigger reopen on every new range; OBS SDK or service-side anomalies returning 200 with no content stream; mocked/stubbed OBS clients in tests that forget to set object content; version mismatches between the connector and the OBS SDK jar.

Common situations: Upgrading the esdk-obs-java SDK to a version whose getObject response wrapper behaves differently; classpath conflicts mixing OBS SDK versions (shaded vs non-shaded); intermittent gateway/proxy behavior stripping response bodies; test fixtures returning partially-built ObjectMetadata.

Related errors


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