{"record":{"id":"30a607ed863fb087","repo":"phacility/phabricator","slug":"expected-s-30a607","errorCode":null,"errorMessage":"Expected `%s`!","messagePattern":"Expected `(.+?)`!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php","lineNumber":147,"sourceCode":"            'Client closed connection before sending a complete protocol '.\n            'frame.'));\n      }\n\n      // If the client has disconnected, kill the subprocess and bail.\n      if (!$io_channel->isOpenForWriting()) {\n        throw new Exception(\n          pht(\n            'Client closed connection before receiving response.'));\n      }\n    }\n  }\n\n  protected function executeRepositoryOperations() {\n    $repository = $this->getRepository();\n\n    $args = $this->getArgs();\n    if (!$args->getArg('tunnel')) {\n      throw new Exception(pht('Expected `%s`!', 'svnserve -t'));\n    }\n\n    if ($this->shouldProxy()) {\n      // NOTE: We're always requesting a writable device here. The request\n      // might be read-only, but we can't currently tell, and SVN requests\n      // can mix reads and writes.\n      $command = $this->getProxyCommand(true);\n      $this->isProxying = true;\n      $cwd = null;\n    } else {\n      $command = csprintf(\n        'svnserve -t --tunnel-user=%s',\n        $this->getSSHUser()->getUsername());\n      $cwd = PhabricatorEnv::getEmptyCWD();\n    }\n\n    $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);\n    $future = new ExecFuture('%C', $command);","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/diffusion/ssh/DiffusionSubversionServeSSHWorkflow.php#L129-L165","documentation":"executeRepositoryOperations() requires the SSH command to be 'svnserve' with the tunnel flag: the workflow registers a single 'tunnel/-t' argument and throws 'Expected svnserve -t!' when it is absent. That argument is exactly how the standard svn+ssh transport invokes the server, so anything else indicates a hand-rolled tunnel command.","triggerScenarios":"Manual 'ssh phabricator@host svnserve' without -t; a custom tunnel in the [tunnels] section of ~/.subversion/config that replaces or adds arguments (e.g. -r /var/repos); wrappers around the svn ssh tunnel that drop -t.","commonSituations":"Developers hand-building tunnels to add ports or users; recipes carried over from plain-svnserve servers that rooted the repo with -r; documentation copy-paste of old svn+ssh snippets.","solutions":["Use plain svn+ssh://user@host/REPO URLs so the client sends exactly 'svnserve -t'","Edit the [tunnels] section of ~/.subversion/config and strip extra arguments from the tunnel definition","Smoke-test with 'ssh user@host svnserve -t' (should connect silently) before retrying svn"],"exampleFix":"# before: custom tunnel with extra args\n# ~/.subversion/config: ssh = ssh -p 2222 phab@host svnserve -r /var/repos\nsvn checkout svn+ssh://phab@host/var/repos/REPO\n\n# after: standard builtin tunnel, no rewriting\n# (remove the custom 'ssh' tunnel entry or keep only port flags)\nsvn checkout svn+ssh://phab@host/REPO","handlingStrategy":"validation","validationCode":"# The SVN tunnel command must be exactly 'svnserve -t' (no -r, no paths).\n# Verify before connecting:\n$ ssh phab@host svnserve -t   # connects silently -> tunnel shape is correct","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer the repository's documented builtin svn+ssh URI over hand-built tunnels","Smoke-test 'ssh user@host svnserve -t' after any tunnel configuration change"],"tags":["phabricator","svn","svnserve","ssh","arguments","tunnel"],"backgroundTag":"invalid-command-arguments","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}