{"record":{"id":"fd7b9b5c6eeef5ed","repo":"apache/iceberg","slug":"invalid-planstatus-s-for-planid-s","errorCode":null,"errorMessage":"Invalid planStatus: %s for planId: %s","messagePattern":"Invalid planStatus: (.+?) for planId: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/rest/RESTTableScan.java","lineNumber":224,"sourceCode":"            ErrorHandlers.tableErrorHandler(),\n            stringStringMap -> {},\n            parserContext);\n\n    this.planId = response.planId();\n    PlanStatus planStatus = response.planStatus();\n    this.scanFileIO =\n        !response.credentials().isEmpty() ? scanFileIO(response.credentials()) : table().io();\n\n    switch (planStatus) {\n      case COMPLETED:\n        return scanTasksIterable(response.planTasks(), response.fileScanTasks());\n      case SUBMITTED:\n        Endpoint.check(supportedEndpoints, Endpoint.V1_FETCH_TABLE_SCAN_PLAN);\n        return fetchPlanningResult();\n      case FAILED:\n        throw new IllegalStateException(failureMessage(planId, response.errorResponse()));\n      default:\n        throw new IllegalStateException(\n            String.format(\"Invalid planStatus: %s for planId: %s\", planStatus, planId));\n    }\n  }\n\n  private FileIO scanFileIO(List<Credential> storageCredentials) {\n    ImmutableMap.Builder<String, String> builder =\n        ImmutableMap.<String, String>builder().putAll(catalogProperties);\n    if (null != planId) {\n      builder.put(RESTCatalogProperties.REST_SCAN_PLAN_ID, planId);\n    }\n\n    Map<String, String> properties = builder.buildKeepingLast();\n    FileIO ioForScan =\n        CatalogUtil.loadFileIO(\n            catalogProperties.getOrDefault(CatalogProperties.FILE_IO_IMPL, DEFAULT_FILE_IO_IMPL),\n            properties,\n            hadoopConf,\n            storageCredentials.stream()","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/rest/RESTTableScan.java#L206-L242","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Upgrade the Iceberg client so its PlanStatus enum knows the server's status value","Fix the server/proxy to emit only spec-defined planStatus values","Validate the response body with curl against the REST spec","Capture the full response for the planId and compare with the OpenAPI spec"],"exampleFix":"// before: client enum lacks new status\n// after: upgrade iceberg-core to a version whose PlanStatus includes the value,\n// or pin server to emit standard statuses:\n//   {\"plan-status\": \"completed\", ...}","handlingStrategy":"try-catch","validationCode":"// validate response shape before interpreting\nPlanStatus status = response.planStatus();\nboolean known = status != null && (status == PlanStatus.COMPLETED || status == PlanStatus.SUBMITTED || status == PlanStatus.FAILED);","typeGuard":"boolean isKnownPlanStatus(FetchScanPlanningResult r) {\n  return r != null && r.planStatus() != null\n    && EnumSet.of(PlanStatus.COMPLETED, PlanStatus.SUBMITTED, PlanStatus.FAILED, PlanStatus.CANCELLED)\n        .contains(r.planStatus());\n}","tryCatchPattern":"try { scan.planFiles(); } catch (IllegalStateException e) {\n  if (e.getMessage().startsWith(\"Invalid planStatus\")) { /* upgrade client / inspect server response */ }\n  else throw e;\n}","preventionTips":["Pin client and server to the same Iceberg REST spec version","Avoid proxies that rewrite JSON responses","Test against the real catalog, not hand-rolled mocks"],"tags":["rest-catalog","scan-planning","protocol-mismatch","enum"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}