phacility/phabricator · error · Exception

Unknown Mercurial command "%s"!

Error message

Unknown Mercurial command "%s"!

What it means

When Phabricator serves Mercurial over its SSH protocol endpoint, DiffusionMercurialWireProtocol keeps a fixed table of the wire commands it understands (between, branchmap, heads, hello, known, listkeys, lookup, pushkey, protocaps, stream_out, unbundle, ...). A client sending a command outside this table gets a hard 'Unknown Mercurial command' Exception before anything executes, because Phabricator must classify every wire command as read-only or mutating before serving it.

Source

Thrown at src/applications/diffusion/protocol/DiffusionMercurialWireProtocol.php:32

      'branches' => array('nodes'),
      'capabilities' => array(),
      'changegroup' => array('roots'),
      'changegroupsubset' => array('bases heads'),
      'debugwireargs' => array('one two *'),
      'getbundle' => array('*'),
      'heads' => array(),
      'hello' => array(),
      'known' => array('nodes', '*'),
      'listkeys' => array('namespace'),
      'lookup' => array('key'),
      'pushkey' => array('namespace', 'key', 'old', 'new'),
      'protocaps' => array('caps'),
      'stream_out' => array(''),
      'unbundle' => array('heads'),
    );

    if (!isset($commands[$command])) {
      throw new Exception(
        pht(
          'Unknown Mercurial command "%s"!',
          $command));
    }

    return $commands[$command];
  }

  public static function isReadOnlyCommand($command) {
    $read_only = array(
      'between' => true,
      'branchmap' => true,
      'branches' => true,
      'capabilities' => true,
      'changegroup' => true,
      'changegroupsubset' => true,
      'debugwireargs' => true,
      'getbundle' => true,

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Use a Mercurial client version compatible with your Phabricator release (inspect the command table in src/applications/diffusion/protocol/DiffusionMercurialWireProtocol.php)
  2. Update Phabricator: newer releases extend the command table as Mercurial evolves
  3. Verify the client is speaking to the correct protocol endpoint (hg vs git vs svn) for the repository type
Defensive patterns

Strategy: validation

Validate before calling

// server-side: check command membership before dispatch
$known = array('between','branchmap','heads','hello','known','listkeys','lookup','pushkey','protocaps','stream_out','unbundle');
if (!in_array($command, $known, true)) {
  // return a clean protocol error to the client instead of an unhandled throw
}

Type guard

function isKnownMercurialWireCommand($cmd) {
  return in_array($cmd, array('between','branchmap','heads','hello','known','listkeys','lookup','pushkey','protocaps','stream_out','unbundle'), true);
}

Try / catch

try {
  $result = DiffusionMercurialWireProtocol::isReadOnlyCommand($command);
} catch (Exception $ex) {
  // reply with a formatted protocol error and close the session; a bad command must not kill the ssh daemon
}

Prevention

When it happens

Trigger: An hg client (or raw protocol script) sends a wire command missing from the table: a newer Mercurial emitting a capability command this Phabricator release never listed, a script speaking the ssh:// protocol with a typo'd command, or a non-Mercurial client or scanner sending garbage to the endpoint.

Common situations: Client hg version newer than the Phabricator release; connecting with git tooling to an hg endpoint; automated probes or health checks hitting the Mercurial ssh service with junk.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/efef96a92bfa2ef1. Report an issue: GitHub.