{"record":{"id":"41633fcdca12b77c","repo":"phacility/phabricator","slug":"unexpected-end-of-file","errorCode":null,"errorMessage":"Unexpected end of file.","messagePattern":"Unexpected end of file\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php","lineNumber":762,"sourceCode":"                $expect,\n                $line));\n          }\n          $mode = 'list2';\n          break;\n        case 'list2':\n          if (!preg_match('/^\\s+path=\"/', $line)) {\n            throw new Exception(\n              pht(\n                \"Expected '%s', got %s.\",\n                '   path=...',\n                $line));\n          }\n          $mode = 'entry-or-end';\n          break;\n      }\n    }\n    if (!$done) {\n      throw new Exception(pht('Unexpected end of file.'));\n    }\n\n    return $map;\n  }\n\n  // TODO: Replace with DiffusionPathIDQuery::getParentPath().\n  private function getParentPath($path) {\n    $path = rtrim($path, '/');\n    $path = dirname($path);\n    if (!$path) {\n      $path = '/';\n    }\n    return $path;\n  }\n\n  // TODO: Replace with DiffusionPathIDQuery::expandPathToRoot().\n  private function expandAllParentPaths($path, $include_self = false) {\n    $parents = array();","sourceCodeStart":744,"sourceCodeEnd":780,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/repository/worker/commitchangeparser/PhabricatorRepositorySvnCommitChangeParserWorker.php#L744-L780","documentation":"The SVN recursive-listing parser reached end of file without ever seeing the closing `</list>` element that sets $done = true. This means the `svn ls -R --xml` output (or its cached copy in /tmp/diffusion.*.svnls) was truncated, empty, or never entered a valid entry loop - the state machine ran out of input while still expecting more XML.","triggerScenarios":"Empty or partial capture file: the `--xml ls -R %s > %s` redirect produced zero bytes (auth prompt failed silently) or the TempFile was moved to the cache before fully flushed; daemon killed mid-write leaving a truncated cache; enormous repositories where svn was interrupted (timeout, OOM) before emitting `</list>`; XML output whose structure never matched earlier states so `$done` could not be set.","commonSituations":"phd task-daemon restarted or OOM-killed while parsing a giant SVN checkout (the comment in the code notes >1GB listings from Facebook-scale repos); disk full in /tmp so the redirect produced a short file; svn exiting non-zero with stderr not captured, leaving an empty stdout file that then gets cached and reused on later runs.","solutions":["Inspect the cache file named in the exception context: `ls -la /tmp/diffusion.*.svnls` and `tail -5` it - if it is empty or lacks `</list>`, delete it and re-run.","Check free space in sys_get_temp_dir() and raise phd memory limits (phd.memory-limit config or systemd limits) for huge repositories.","Re-run `svn ls -R --xml <uri>@<rev> | tail -1` manually to confirm svn itself completes and ends with `</list>`; if it does not, fix svn-side issues (network, auth, timeouts).","If output is complete but the parser bails early on a mismatch, fix the earlier 'Expected ...' error instead - the EOF is only a downstream symptom."],"exampleFix":"// before: an empty capture is cached and only fails later at EOF\nif (!Filesystem::pathExists($cache_loc)) {\n  $tmp = new TempFile();\n  $repository->execxRemoteCommand('--xml ls -R %s > %s', $path_uri, $tmp);\n  execx('mv %s %s', $tmp, $cache_loc);\n}\n\n// after: refuse to cache an output that never closed the list\nif (!Filesystem::pathExists($cache_loc)) {\n  $tmp = new TempFile();\n  $repository->execxRemoteCommand('--xml ls -R %s > %s', $path_uri, $tmp);\n  if (strpos(Filesystem::readTail($tmp, 64), '</list>') === false) {\n    throw new Exception(pht('svn ls -R output is truncated.'));\n  }\n  execx('mv %s %s', $tmp, $cache_loc);\n}","handlingStrategy":"validation","validationCode":"// Reject/truncate-cache before parsing: require the closing tag\n$tail = execx('tail -c 64 %s', $cache_loc);\nif (strpos($tail[0], '</list>') === false) {\n  Filesystem::remove($cache_loc);\n  throw new Exception('Refusing truncated svn listing cache.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  $map = $this->parseRecursiveListFileData($cache_loc);\n} catch (Exception $ex) {\n  if (strpos($ex->getMessage(), 'Unexpected end of file') !== false) {\n    Filesystem::remove($cache_loc); // stale truncated cache\n  }\n  throw $ex;\n}","preventionTips":["Never cache partial command output - verify the terminator before mv into the cache slot","Disk-space alert on the tmp filesystem used by daemon workers","Watch for OOM kills of phd daemons during giant-repo imports"],"tags":["phabricator","svn","truncated-output","cache","daemon"],"backgroundTag":"truncated-upstream-output","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}