{"record":{"id":"c30039d1c2c925d0","repo":"phacility/phabricator","slug":"the-regular-expression-s-is-not-valid-regular","errorCode":null,"errorMessage":"The regular expression \"%s\" is not valid. Regular expressions must have enclosing characters (e.g. \"@/path/to/file@\", not \"/path/to/file\") and be syntactically correct.","messagePattern":"The regular expression \"(.+?)\" is not valid\\. Regular expressions must have enclosing characters \\(e\\.g\\. \"@/path/to/file@\", not \"/path/to/file\"\\) and be syntactically correct\\.","errorType":"validation","errorClass":"HeraldInvalidConditionException","httpStatus":null,"severity":"error","filePath":"src/applications/herald/adapter/HeraldAdapter.php","lineNumber":622,"sourceCode":"        return (($condition_value & $field_value) === (int)$condition_value);\n      case self::CONDITION_NOT_BIT:\n        return (($condition_value & $field_value) !== (int)$condition_value);\n      default:\n        throw new HeraldInvalidConditionException(\n          pht(\"Unknown condition '%s'.\", $condition_type));\n    }\n  }\n\n  public function willSaveCondition(HeraldCondition $condition) {\n    $condition_type = $condition->getFieldCondition();\n    $condition_value = $condition->getValue();\n\n    switch ($condition_type) {\n      case self::CONDITION_REGEXP:\n      case self::CONDITION_NOT_REGEXP:\n        $ok = @preg_match($condition_value, '');\n        if ($ok === false) {\n          throw new HeraldInvalidConditionException(\n            pht(\n              'The regular expression \"%s\" is not valid. Regular expressions '.\n              'must have enclosing characters (e.g. \"@/path/to/file@\", not '.\n              '\"/path/to/file\") and be syntactically correct.',\n              $condition_value));\n        }\n        break;\n      case self::CONDITION_REGEXP_PAIR:\n        $json = null;\n        try {\n          $json = phutil_json_decode($condition_value);\n        } catch (PhutilJSONParserException $ex) {\n          throw new HeraldInvalidConditionException(\n            pht(\n              'The regular expression pair \"%s\" is not valid JSON. Enter a '.\n              'valid JSON array with two elements.',\n              $condition_value));\n        }","sourceCodeStart":604,"sourceCodeEnd":640,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/herald/adapter/HeraldAdapter.php#L604-L640","documentation":"Thrown by HeraldAdapter::willSaveCondition() when a Herald rule with a 'matches regular expression' (CONDITION_REGEXP) or 'does not match regular expression' (CONDITION_NOT_REGEXP) condition is saved and PHP's preg_match() rejects the pattern. PCRE patterns in PHP must be wrapped in delimiter characters (e.g. '@' or '/'), so Phabricator deliberately runs a smoke-test match on the empty string and treats a boolean false return as an invalid pattern. The message shows the offending value and reminds you that delimiters are required (e.g. '@/path/to/file@').","triggerScenarios":"Saving any Herald rule whose condition value is a regex without enclosing delimiters (e.g. 'foo.*bar' instead of '@foo.*bar@'), with unbalanced or mismatched delimiters, with an unknown PCRE modifier (e.g. '@foo@z'), or with a syntax error inside the pattern. Triggered from the rule edit UI ('rule' JSON blob POST to /herald/edit/) or from any code path that calls HeraldEditor/HeraldRule save with a CONDITION_REGEXP condition.","commonSituations":"Users copy a regex from grep, JavaScript, or Python where delimiters are not used, and paste it directly into the Herald condition box. Typos in modifiers ('@pattern@i' mistyped as '@pattern@1'), forgetting the closing delimiter, or copy-pasting a pattern that contains the delimiter character unescaped are the usual causes.","solutions":["Wrap the pattern in matching delimiter characters, e.g. change foo.*bar to @foo.*bar@ (or /foo.*/bar/ with the inner slash escaped).","If the pattern contains the delimiter, either escape it (\\/) or pick a different delimiter (@, #, ~, %).","Sanity-check the pattern locally before saving: run php -r 'var_dump(@preg_match($argv[1], \"\"));' -- '@your@pattern@' — it must return int(0) or int(1), not bool(false).","Check the trailing modifiers (i, m, s, u, x) are valid PCRE modifiers and spelled correctly."],"exampleFix":"// before (condition value saved in Herald UI)\nfoo/src/.*\\.php\n\n// after — delimiters added, inner delimiter escaped\n@foo/src/.*\\.php@","handlingStrategy":"validation","validationCode":"// Validate before saving the rule / condition\n$value = $condition->getValue();\nif (@preg_match($value, '') === false) {\n  // reject with a form error; do NOT call HeraldEditor->save()\n  return pht('Invalid regexp: add delimiters, e.g. @pattern@');\n}","typeGuard":"function isValidHeraldRegexp($pattern) {\n  return is_string($pattern) && @preg_match($pattern, '') !== false;\n}","tryCatchPattern":"// If you call code that saves rules and cannot pre-validate:\ntry {\n  $editor->save();\n} catch (HeraldInvalidConditionException $ex) {\n  // surface $ex->getMessage() as a field-level validation error,\n  // re-prompting the user with their input preserved\n}","preventionTips":["Standardize on '@' delimiters for every Herald regex so '/' in paths never needs escaping.","Lint regex conditions in a pre-save hook with @preg_match($value, '') before the HeraldEditor runs.","When importing rules from other tools, run patterns through a converter that adds delimiters and escapes."],"tags":["herald","phabricator","php","regex","pcre","validation","rule-engine"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}