{"record":{"id":"36fc9347335dca3c","repo":"phacility/phabricator","slug":"unexpected-command-structure-expected-s","errorCode":null,"errorMessage":"Unexpected command structure, expected '%s'.","messagePattern":"Unexpected command structure, expected '(.+?)'\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/diffusion/protocol/DiffusionSubversionWireProtocol.php","lineNumber":167,"sourceCode":"        default:\n          throw new Exception(\n            pht(\n              \"Unknown SVN wire protocol structure '%s'!\",\n              $type));\n      }\n      if ($type != 'list') {\n        $out[] = ' ';\n      }\n    }\n    $out[] = ') ';\n\n    return implode('', $out);\n  }\n\n  public function isReadOnlyCommand(array $struct) {\n    if (empty($struct[0]['type']) || ($struct[0]['type'] != 'word')) {\n      // This isn't what we expect; fail defensively.\n      throw new Exception(\n        pht(\n          \"Unexpected command structure, expected '%s'.\",\n          '( word ... )'));\n    }\n\n    switch ($struct[0]['value']) {\n      // Authentication command set.\n      case 'EXTERNAL':\n\n      // The \"Main\" command set. Some of the commands in this command set are\n      // mutation commands, and are omitted from this list.\n      case 'reparent':\n      case 'get-latest-rev':\n      case 'get-dated-rev':\n      case 'rev-proplist':\n      case 'rev-prop':\n      case 'get-file':\n      case 'get-dir':","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/diffusion/protocol/DiffusionSubversionWireProtocol.php#L149-L185","documentation":"Phabricator's Subversion-over-SSH protocol handler parses each client command into a nested ( ... ) structure. isReadOnlyCommand() requires element 0 of that structure to be a token of type 'word' (the command name, like EXTERNAL or reparent) so it can classify the command; any other shape fails defensively with an Exception instead of being guessed read-only.","triggerScenarios":"A client sends a command whose first parsed element is not a word: non-svn input hitting the svn endpoint, a corrupted or desynchronized protocol stream, truncated stdin over ssh, or a parser edge case.","commonSituations":"Scanners or health checks sending garbage bytes to the ssh service; unusual svn client dialects; middleboxes corrupting the ssh stream.","solutions":["Reproduce with a stock svn client; if stock svn works, capture the raw bytes the failing client sends","Check for proxies or wrapper scripts corrupting ssh stdin","If a normal svn client triggers it, file a bug upstream with the captured payload"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// validate the parsed structure before classification\nif (!isset($struct[0]['type']) || $struct[0]['type'] !== 'word') {\n  // reject the command with a protocol error\n}","typeGuard":"function isSvnWordCommandStruct(array $struct) {\n  return isset($struct[0]['type']) && $struct[0]['type'] === 'word';\n}","tryCatchPattern":"try {\n  $read_only = $protocol->isReadOnlyCommand($struct);\n} catch (Exception $ex) {\n  // reject the client request with a protocol error; keep the ssh service running\n}","preventionTips":["Health-check the svn endpoint with a real svn client, not raw TCP probes","Log malformed structures with their raw payload to catch middlebox corruption early"],"tags":["subversion","wire-protocol","parsing","phabricator"],"backgroundTag":"malformed-protocol-message","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}