{"record":{"id":"fd70fb582d58d4c0","repo":"apache/druid","slug":"failed-to-reserve-partial-metadata-for-segment-s","errorCode":null,"errorMessage":"Failed to reserve partial metadata for segment[%s] on location[%s] during bootstrap","messagePattern":"Failed to reserve partial metadata for segment\\[(.+?)\\] on location\\[(.+?)\\] during bootstrap","errorType":"exception","errorClass":"SegmentLoadingException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/segment/loading/SegmentLocalCacheManager.java","lineNumber":1716,"sourceCode":"        // load fetches it fresh rather than reserving an entry that could never fetch anything. The info file goes\n        // with it: it asserts that this segment has local cache state, which stops being true here, and nothing else\n        // will remove it (no entry was reserved, so there is no unmount hook to fire).\n        log.warn(\n            \"On-disk partial-load layout for segment[%s] in [%s] has no usable range reader (this should not \"\n            + \"happen); deleting it so the segment can be re-loaded.\",\n            dataSegment.getId(),\n            partialDir\n        );\n        atomicMoveAndDeleteCacheEntryDirectory(partialDir);\n        deleteSegmentInfoFile(dataSegment);\n        throw new SegmentLoadingException(\n            \"No usable range reader for partial segment[%s]; its local layout has been reclaimed\",\n            dataSegment.getId()\n        );\n      }\n      final ReservedPartial reserved = tryReservePartialAt(dataSegment, rangeReader, location, false);\n      if (reserved == null) {\n        throw new SegmentLoadingException(\n            \"Failed to reserve partial metadata for segment[%s] on location[%s] during bootstrap\",\n            dataSegment.getId(),\n            location.getPath()\n        );\n      }\n      return reserved.hold;\n    }\n    return null;\n  }\n\n  /**\n   * Reserve a {@link CompleteSegmentCacheEntry} for an eagerly-downloaded segment whose files are on disk, so\n   * {@link #bootstrap} has something to mount, and hand back the hold it was reserved under. Returns {@code null} when\n   * this segment has no complete layout on disk, or already has an entry. Virtual storage only: the legacy path\n   * reserves these statically in {@link #getCachedSegments} and never evicts them.\n   * <p>\n   * Reserving under a hold is what makes the mount safe: reclaim skips held entries only, so an unheld entry can be\n   * selected as a victim while a parallel bootstrap thread is still mounting it. Releasing the hold once mounted","sourceCodeStart":1698,"sourceCodeEnd":1734,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/segment/loading/SegmentLocalCacheManager.java#L1698-L1734","documentation":"Thrown by reservePartialForBootstrap when a valid partial segment layout exists on disk and a range reader opened successfully, but tryReservePartialAt could not reserve the partial metadata entry on the location. Reservation can fail due to eviction pressure, lock contention, or the location refusing to track the new entry during bootstrap.","triggerScenarios":"During bootstrap of a location containing a partial segment layout, tryReservePartialAt(dataSegment, rangeReader, location, false) returns null — e.g. the location's tracker cannot add the reservation (size accounting failure, concurrent eviction, or entry already being processed).","commonSituations":"Cache locations at or near maxSize capacity during historical startup; multiple bootstrap/recovery paths racing over the same segment; disk accounting inconsistencies left by a previous crash.","solutions":["Increase druid.segmentCache.locations maxSize (or free disk) so the location can accept the reservation.","Check logs for concurrent eviction activity targeting the same segment during startup; restart after quieting load/eviction churn.","Manually remove the affected partial directory so bootstrap skips the broken reservation and the segment re-downloads.","Verify location disks are healthy and not over-capacity (du vs configured maxSize)."],"exampleFix":"// before\ndruid.segmentCache.locations=[{\"path\":\"/var/druid/segment-cache\",\"maxSize\":10000000000}]\n// after\ndruid.segmentCache.locations=[{\"path\":\"/var/druid/segment-cache\",\"maxSize\":50000000000}]","handlingStrategy":"try-catch","validationCode":"// ensure each cache location's maxSize exceeds the sum of segment sizes on disk\nlong onDisk = Files.walk(locationPath).mapToLong(p -> p.toFile().length()).sum();\nif (onDisk >= maxSize) { throw new IllegalStateException(\"maxSize too small for existing cache\"); }","typeGuard":null,"tryCatchPattern":"try { bootstrap(); } catch (SegmentLoadingException e) { if (e.getMessage().contains(\"Failed to reserve partial metadata\")) { cleanupPartialDir(segmentId); } }","preventionTips":["Size druid.segmentCache.locations maxSize with headroom above the expected cache footprint.","Ensure clean shutdowns so reservation accounting is not corrupted.","Alert on cache locations exceeding ~90% of maxSize."],"tags":["segment-cache","bootstrap","reservation","disk-capacity"],"backgroundTag":"internal-invariant-violation","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"}