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
- 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
- Retry the read operation: this is not deterministic state corruption — the next reopen usually succeeds; wrap positional reads in a bounded retry
- If behind a proxy/gateway, inspect whether ranged GET responses are being altered (e.g. 200-without-body instead of 206)
- 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
- Pin the OBS SDK version required by the connector; check for duplicate SDK jars
- Cover ranged GetObject in test mocks so content is never null
- Monitor frequency: recurring null-content errors indicate gateway/proxy or version problems
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
- read failed of {}, inputStream is {}
- Received end of stream result before all requestedBytes were
- End of file reached before reading fully.
- Retry " + retry + " times to read still exception: " + error
- Exception occurred while closing channel '%s'
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/ab6d1cb6a43eaf74.
Report an issue: GitHub.