{"record":{"id":"b58e232664106222","repo":"apache/druid","slug":"end-metadata-cannot-be-null","errorCode":null,"errorMessage":"end metadata cannot be null","messagePattern":"end metadata cannot be null","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinator.java","lineNumber":620,"sourceCode":"  @Override\n  public SegmentPublishResult commitMetadataOnly(\n      String supervisorId,\n      String dataSource,\n      DataSourceMetadata startMetadata,\n      DataSourceMetadata endMetadata\n  )\n  {\n    if (supervisorId == null) {\n      throw new IllegalArgumentException(\"supervisorId cannot be null\");\n    }\n    if (dataSource == null) {\n      throw new IllegalArgumentException(\"datasource name cannot be null\");\n    }\n    if (startMetadata == null) {\n      throw new IllegalArgumentException(\"start metadata cannot be null\");\n    }\n    if (endMetadata == null) {\n      throw new IllegalArgumentException(\"end metadata cannot be null\");\n    }\n\n    try {\n      return inReadWriteDatasourceTransaction(\n          dataSource,\n          transaction -> updateDataSourceMetadataInTransaction(\n              transaction,\n              supervisorId,\n              dataSource,\n              startMetadata,\n              endMetadata\n          )\n      );\n    }\n    catch (CallbackFailedException e) {\n      throw e;\n    }\n  }","sourceCodeStart":602,"sourceCodeEnd":638,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinator.java#L602-L638","documentation":"commitMetadataOnly() throws IllegalArgumentException when endMetadata is null. The end metadata is the new value written by the transactional update, so a null target would either NPE inside the update or wipe the metadata; the coordinator rejects it at the entry point instead.","triggerScenarios":"Calling commitMetadataOnly with a null endMetadata — typically a builder or conversion step producing null metadata (e.g. empty partition/offset map turned into a null object) passed straight through.","commonSituations":"Custom checkpoint logic constructing the new metadata conditionally and hitting a null branch; mapping code that returns null instead of a default metadata object; version-upgrade code paths where a field was renamed and no longer populated.","solutions":["Always construct a concrete end metadata object (use defaults/empty collections rather than null).","Assert endMetadata non-null at the call site before invoking the coordinator.","Trace the metadata-producing code path to find which step returned null and fix it."],"exampleFix":"// before\nMetadata endMeta = computeNewMetadata(...); // may return null\ncoordinator.commitMetadataOnly(dataSource, id, startMeta, endMeta);\n// after\nMetadata endMeta = Optional.ofNullable(computeNewMetadata(...)).orElseGet(Metadata::new);\ncoordinator.commitMetadataOnly(dataSource, id, startMeta, endMeta);","handlingStrategy":"validation","validationCode":"if (endMetadata == null) {\n  throw new IllegalArgumentException(\"endMetadata must be provided\");\n}","typeGuard":"boolean hasEndMetadata(Metadata m) { return m != null; }","tryCatchPattern":"try {\n  coordinator.commitMetadataOnly(dataSource, id, startMeta, endMeta);\n} catch (IllegalArgumentException e) {\n  log.error(e, \"Invalid end metadata: %s\", e.getMessage());\n}","preventionTips":["Use Optional.ofNullable(...).orElseGet(Metadata::new) when computing new metadata","Avoid methods that may return null Metadata; prefer defaults","Assert non-null in tests for every metadata-producing code path"],"tags":["metadata","null-argument","supervisor"],"backgroundTag":"null-argument","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}