apache/iceberg · error · RESTException

Invalid (null) response body for request

Error message

Invalid (null) response body for request (expected %s): method=%s, path=%s, status=%d

What it means

handleResponse expects a JSON body for the given responseType after error handling did not throw. If the response has no entity at all (null entity) while a non-empty-body type like LoadTableResponse was expected, the client throws RESTException. It indicates the server responded successfully (or with a non-error status) but without the promised payload.

Solutions

  1. Verify with curl that the endpoint actually returns a JSON body for the same request.
  2. Fix the server/route so it returns the expected response type serialized as JSON.
  3. Check for redirect/middleware/proxy configurations that may strip response bodies.
  4. Confirm the client is pointing at the right endpoint (path and method) for the operation.

Example fix

// before: server returns 200 with no body
return Response.ok().build();
// after: always serialize the expected response
return Response.ok(LoadTableResponse.builder()
    .withTableMetadata(metadata)
    .build()).build();
Defensive patterns

Strategy: try-catch

Try / catch

try {
  LoadTableResponse resp = client.post(path, body, LoadTableResponse.class, headers);
} catch (RESTException e) {
  if (e.getMessage().contains("null) response body")) {
    // server returned success without a payload — inspect endpoint/server implementation
  }
  throw e;
}

Prevention

When it happens

Trigger: The server returns a success status (e.g. 200/201) with no response entity for an endpoint the client expects to deserialize into responseType (like a table or namespace metadata response), and emptyBody() does not consider this response empty.

Common situations: Misbehaving or partially implemented catalog server; wrong endpoint routed (e.g. loadTable hit a route that only acknowledges); server stripped the body on redirect/replay; custom catalog implementation forgot to serialize the response.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/5a1bf16d4a057a60. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/rest/HTTPClient.java:369

        // 304-NOT_MODIFIED is used for freshness-aware loading and requires an ETag sent to the
        // server via IF_NONE_MATCH header in the request. If no ETag was sent, we shouldn't
        // receive a 304.
        throw new RESTException(
            "Invalid (NOT_MODIFIED) response for request: method=%s, path=%s",
            request.method(), request.path());
      }

      return null;
    }

    if (!isSuccessful(response)) {
      // The provided error handler is expected to throw, but a RESTException is thrown if not.
      String responseBody = extractResponseBodyAsString(response);
      throwFailure(response, responseBody, errorHandler);
    }

    if (response.getEntity() == null) {
      throw new RESTException(
          "Invalid (null) response body for request (expected %s): method=%s, path=%s, status=%d",
          responseType.getSimpleName(), request.method(), request.path(), response.getCode());
    }

    ObjectReader reader = objectReaderCache.computeIfAbsent(responseType, mapper::readerFor);
    if (parserContext != null && !parserContext.isEmpty()) {
      reader = reader.with(parserContext.toInjectableValues());
    }
    return reader.readValue(response.getEntity().getContent());
  }

  private <T extends RESTResponse> boolean emptyBody(
      ClassicHttpResponse response, Class<T> responseType) {
    return response.getCode() == HttpStatus.SC_NO_CONTENT
        || response.getCode() == HttpStatus.SC_NOT_MODIFIED
        || (responseType == null && isSuccessful(response));
  }

View on GitHub (pinned to 86d9c8fc54)