{"record":{"id":"03620c3d08f16ab3","repo":"phacility/phabricator","slug":"unable-to-find-fetch","errorCode":null,"errorMessage":"Unable to find fetch!","messagePattern":"Unable to find fetch!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php","lineNumber":172,"sourceCode":"      $anchor_byte = $view['offset'];\n\n      if ($view['direction'] < 0) {\n        $anchor_byte = $anchor_byte - 1;\n      }\n\n      $data_key = null;\n      foreach ($reads as $read_key => $read) {\n        $s = $read['fetchOffset'];\n        $e = $s + $read['fetchLength'];\n\n        if (($s <= $anchor_byte) && ($e >= $anchor_byte)) {\n          $data_key = $read_key;\n          break;\n        }\n      }\n\n      if ($data_key === null) {\n        throw new Exception(\n          pht('Unable to find fetch!'));\n      }\n\n      $anchor_key = null;\n      foreach ($reads[$data_key]['lines'] as $line_key => $line) {\n        $s = $line['offset'];\n        $e = $s + $line['length'];\n\n        if (($s <= $anchor_byte) && ($e > $anchor_byte)) {\n          $anchor_key = $line_key;\n          break;\n        }\n      }\n\n      if ($anchor_key === null) {\n        throw new Exception(\n          pht(\n            'Unable to find lines.'));","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php#L154-L190","documentation":"Thrown by the build-log rendering controller after it computes the byte-range reads (head window, tail window, highlight ranges) it will fetch. Each read produces a [fetchOffset, fetchOffset+fetchLength] window clamped to the current log size; this exception means the view's anchor byte (the requested offset, minus 1 for tail views) does not fall inside any computed window. It is an internal invariant failure: the offset the caller asked for no longer lies in the data that was actually read, almost always because the log's length changed between the request parameters being generated and the data being fetched.","triggerScenarios":"Opening /harbormaster/log/... with stale headOffset/tailOffset or a 'lines' highlight range that lies beyond the log's current byte length: the log was truncated, rotated, or archived/compressed (changing its size) since the page or URL was produced; or a scraper/script replays a render URL with hard-coded offsets after the build log grew or shrank. Also possible when log chunk data is corrupted so the readable data is shorter than the recorded log size.","commonSituations":"A build log page left open in a browser while the daemon archives/compresses the log; log garbage collection shortening a log; automated tools hitting the render endpoint with saved offsets; database chunk corruption after a partial migration.","solutions":["Reload the log page without the offset parameters (fresh /log/... URL) so offsets are recomputed from the current log size.","If it recurs for a specific log, compare the log's recorded byte length with the sum of its stored chunks (harbormaster_buildlogchunk) to detect corruption or a half-finished archival.","If you maintain this code, clamp anchor_byte to [0, log_size-1] (and skip views whose window is empty) instead of throwing, so stale offsets degrade gracefully.","For scrapers, fetch the log metadata/current size before requesting byte ranges, and re-derive offsets each run."],"exampleFix":"// before (HarbormasterBuildLogRenderController.php)\n$anchor_byte = $view['offset'];\nif ($view['direction'] < 0) {\n  $anchor_byte = $anchor_byte - 1;\n}\n// ...\nif ($data_key === null) {\n  throw new Exception(pht('Unable to find fetch!'));\n}\n\n// after\n$anchor_byte = $view['offset'];\nif ($view['direction'] < 0) {\n  $anchor_byte = $anchor_byte - 1;\n}\n$anchor_byte = min(max(0, $anchor_byte), max(0, $log_size - 1));\n// ...window search now always succeeds for nonempty logs","handlingStrategy":"validation","validationCode":"// Before requesting a log render with offsets, re-check them against the\n// log's current size (HarbormasterBuildLogQuery + getTotalByteLength equivalent):\n$size = get_log_byte_length($log); // e.g. via the log's getSize() / chunk sum\n$head_offset = min(max(0, $head_offset), $size);\n$tail_offset = min(max(0, $tail_offset), $size);\nif ($highlight_range) { $highlight_range = clamp_range($highlight_range, 0, $size); }","typeGuard":null,"tryCatchPattern":"try {\n  renderLogViews($log, $views);\n} catch (Exception $e) {\n  // Stale offsets: fall back to a fresh, offset-free render\n  return renderLogViews($log, defaultViews($log));\n}","preventionTips":["Always re-derive head/tail offsets from the log's current byte length instead of reusing cached URLs or offsets.","When scraping render endpoints, fetch log metadata first and clamp all range parameters to [0, size].","Treat log size as volatile during active builds and archival; retry once with fresh parameters before surfacing an error."],"tags":["harbormaster","build-log","rendering","invariant"],"backgroundTag":"stale-byte-offset","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}