apache/iceberg · error · IllegalStateException

Invalid planStatus: for planId

Error message

Invalid planStatus: %s for planId: %s

What it means

Thrown when the FetchScanPlanningResult returns a planStatus that is not one of COMPLETED, SUBMITTED, or FAILED — i.e. the server sent an unknown or null plan status during scan planning. This guards against protocol drift between client and server.

Solutions

  1. Upgrade the Iceberg client so its PlanStatus enum knows the server's status value
  2. Fix the server/proxy to emit only spec-defined planStatus values
  3. Validate the response body with curl against the REST spec
  4. Capture the full response for the planId and compare with the OpenAPI spec

Example fix

// before: client enum lacks new status
// after: upgrade iceberg-core to a version whose PlanStatus includes the value,
// or pin server to emit standard statuses:
//   {"plan-status": "completed", ...}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate response shape before interpreting
PlanStatus status = response.planStatus();
boolean known = status != null && (status == PlanStatus.COMPLETED || status == PlanStatus.SUBMITTED || status == PlanStatus.FAILED);

Type guard

boolean isKnownPlanStatus(FetchScanPlanningResult r) {
  return r != null && r.planStatus() != null
    && EnumSet.of(PlanStatus.COMPLETED, PlanStatus.SUBMITTED, PlanStatus.FAILED, PlanStatus.CANCELLED)
        .contains(r.planStatus());
}

Try / catch

try { scan.planFiles(); } catch (IllegalStateException e) {
  if (e.getMessage().startsWith("Invalid planStatus")) { /* upgrade client / inspect server response */ }
  else throw e;
}

Prevention

When it happens

Trigger: planTableScan/planFiles receives a response whose planStatus is null or an unrecognized value — typically a newer/older server version emitting a status the client enum cannot map, or a buggy/misbehaving proxy rewriting the response.

Common situations: Client and server version mismatch (server added a new plan status), hand-rolled mock REST servers returning malformed plan responses, reverse proxies stripping or corrupting JSON fields.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/rest/RESTTableScan.java:224

            ErrorHandlers.tableErrorHandler(),
            stringStringMap -> {},
            parserContext);

    this.planId = response.planId();
    PlanStatus planStatus = response.planStatus();
    this.scanFileIO =
        !response.credentials().isEmpty() ? scanFileIO(response.credentials()) : table().io();

    switch (planStatus) {
      case COMPLETED:
        return scanTasksIterable(response.planTasks(), response.fileScanTasks());
      case SUBMITTED:
        Endpoint.check(supportedEndpoints, Endpoint.V1_FETCH_TABLE_SCAN_PLAN);
        return fetchPlanningResult();
      case FAILED:
        throw new IllegalStateException(failureMessage(planId, response.errorResponse()));
      default:
        throw new IllegalStateException(
            String.format("Invalid planStatus: %s for planId: %s", planStatus, planId));
    }
  }

  private FileIO scanFileIO(List<Credential> storageCredentials) {
    ImmutableMap.Builder<String, String> builder =
        ImmutableMap.<String, String>builder().putAll(catalogProperties);
    if (null != planId) {
      builder.put(RESTCatalogProperties.REST_SCAN_PLAN_ID, planId);
    }

    Map<String, String> properties = builder.buildKeepingLast();
    FileIO ioForScan =
        CatalogUtil.loadFileIO(
            catalogProperties.getOrDefault(CatalogProperties.FILE_IO_IMPL, DEFAULT_FILE_IO_IMPL),
            properties,
            hadoopConf,
            storageCredentials.stream()

View on GitHub (pinned to 86d9c8fc54)