{"record":{"id":"905652ff758bf81d","repo":"phacility/phabricator","slug":"hasher-s-may-produce-hashes-which-are-too-long","errorCode":null,"errorMessage":"Hasher \"%s\" may produce hashes which are too long to fit in storage. %d characters are available, but its hashes may be up to %d characters in length.","messagePattern":"Hasher \"(.+?)\" may produce hashes which are too long to fit in storage\\. (.+?) characters are available, but its hashes may be up to (.+?) characters in length\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/infrastructure/util/password/PhabricatorPasswordHasher.php","lineNumber":227,"sourceCode":"   * actually be used (for example, a required extension is missing).\n   *\n   * @return list<PhabricatorPasswordHasher> Hasher objects.\n   * @task hashing\n   */\n  public static function getAllHashers() {\n    $objects = id(new PhutilClassMapQuery())\n      ->setAncestorClass(__CLASS__)\n      ->setUniqueMethod('getHashName')\n      ->execute();\n\n    foreach ($objects as $object) {\n      $name = $object->getHashName();\n\n      $potential_length = strlen($name) + $object->getHashLength() + 1;\n      $maximum_length = self::MAXIMUM_STORAGE_SIZE;\n\n      if ($potential_length > $maximum_length) {\n        throw new Exception(\n          pht(\n            'Hasher \"%s\" may produce hashes which are too long to fit in '.\n            'storage. %d characters are available, but its hashes may be '.\n            'up to %d characters in length.',\n            $name,\n            $maximum_length,\n            $potential_length));\n      }\n    }\n\n    return $objects;\n  }\n\n\n  /**\n   * Get all usable password hashers. This may include hashers which are\n   * not desirable or advisable.\n   *","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/infrastructure/util/password/PhabricatorPasswordHasher.php#L209-L245","documentation":"Password hashes are stored as 'name:hash' strings in a fixed-size storage column, capped by PhabricatorPasswordHasher::MAXIMUM_STORAGE_SIZE (128 characters). When hasher classes are discovered (getAllHashers), each one is checked: strlen(getHashName()) + getHashLength() + 1 must fit within 128. A hasher that could exceed the cap is rejected at discovery time so a password can never be hashed into something un-storable.","triggerScenarios":"Registering a custom PhabricatorPasswordHasher subclass whose getHashName() plus getHashLength() totals more than 127 characters — e.g. a long algorithm identifier like 'super-argon2id-v19-m=65536,t=4,p=1' combined with a long raw or hex-encoded hash. The exception is thrown while enumerating hashers, which happens during password operations and authentication setup.","commonSituations":"Writing a custom hasher that returns the full algorithm parameters in getHashName(); returning getHashLength() as the base64/hex length of the encoded output when it should be the trimmed length; wrapping an external library that emits very long composite hashes.","solutions":["Shorten getHashName() to a compact unique identifier (e.g. 'xargon2') and keep parameters inside the hash body only if the total still fits.","Reduce getHashLength() by encoding the digest more compactly (base64 instead of hex halves the length) so name + ':' + digest stays under 128.","If the format genuinely cannot fit in 128 characters, the algorithm is not usable with Phabricator's storage schema — pick a shorter representation.","Add a unit test asserting strlen($name) + $hasher->getHashLength() + 1 <= PhabricatorPasswordHasher::MAXIMUM_STORAGE_SIZE."],"exampleFix":"// before\npublic function getHashName() {\n  return 'argon2id-v=19-m=65536-t=4-p=1';   // 26 chars + long digest\n}\npublic function getHashLength() {\n  return 512;   // hex-encoded digest -> total exceeds 128\n}\n\n// after\npublic function getHashName() {\n  return 'xargon2id';   // parameters live in the digest, not the name\n}\npublic function getHashLength() {\n  return 64;   // raw 32-byte digest, base64url-encoded\n}","handlingStrategy":"validation","validationCode":"// Check a custom hasher fits storage before registering it:\n$name = $hasher->getHashName();\n$length = strlen($name) + $hasher->getHashLength() + 1;\nif ($length > PhabricatorPasswordHasher::MAXIMUM_STORAGE_SIZE) {\n  throw new Exception(\n    pht('Hasher %s needs %d chars but only %d are available.',\n      $name, $length, PhabricatorPasswordHasher::MAXIMUM_STORAGE_SIZE));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep getHashName() short (a few characters) — it is an identifier, not a parameter string.","Encode digests compactly (base64 not hex) and return the true maximum length from getHashLength().","Add a unit test for the 128-character budget so regressions fail in CI, not in production."],"tags":["phabricator","password-hashing","storage-limits","php"],"backgroundTag":"value-exceeds-storage-limit","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}