phacility/phabricator · error · PhabricatorEditorURIParserException

Editor pattern "%s" is invalid: the final character in a pat

Error message

Editor pattern "%s" is invalid: the final character in a pattern may not be an unencoded percent symbol ("%%"). Use "%%%%" to encode a literal percent symbol.

What it means

newPatternTokens() walks the pattern character by character; a '%' consumes the following character as part of a variable token. A '%' in the final position has no following character, so it is ambiguous (a variable name is missing) and the parser throws PhabricatorEditorURIParserException telling you to use %% to encode a literal percent. This is the trailing-edge case of the same tokenizer used for all editor pattern validation.

Source

Thrown at src/infrastructure/editor/PhabricatorEditorURIEngine.php:322

      $result[] = $token_value;
    }

    $result = implode('', $result);

    return $result;
  }

  public static function newPatternTokens($raw_pattern) {
    $token_positions = array();

    $len = strlen($raw_pattern);

    for ($ii = 0; $ii < $len; $ii++) {
      $c = $raw_pattern[$ii];
      if ($c === '%') {
        if (!isset($raw_pattern[$ii + 1])) {
          throw new PhabricatorEditorURIParserException(
            pht(
              'Editor pattern "%s" is invalid: the final character in a '.
              'pattern may not be an unencoded percent symbol ("%%"). '.
              'Use "%%%%" to encode a literal percent symbol.',
              $raw_pattern));
        }

        $token_positions[] = $ii;
        $ii++;
      }
    }

    // Add a final marker past the end of the string, so we'll collect any
    // trailing literal bytes.
    $token_positions[] = $len;

    $tokens = array();
    $cursor = 0;

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Double the trailing percent: '%%' - e.g. 'vim://open?url=file://%f&rate=100%%'.
  2. Audit the pattern for any other lone '%' that is not part of %f/%l/%n/%d/%p/%r/%% and escape it as %%; check both `./bin config get editor` and user preferences.
  3. Validate patterns programmatically with PhabricatorEditorURIEngine::newPatternTokens($pattern) before saving them to config.

Example fix

// before: trailing bare percent
vim://open?url=file://%f&pct=90%

// after: encoded literal percent
vim://open?url=file://%f&pct=90%%
Defensive patterns

Strategy: validation

Validate before calling

if (substr($pattern, -1) === '%') {
  throw new Exception(
    'Pattern may not end with a bare %; use %% for a literal percent.');
}

Try / catch

try {
  PhabricatorEditorURIEngine::newPatternTokens($pattern);
} catch (PhabricatorEditorURIParserException $ex) {
  // keep the previously saved pattern and report the parse error inline
  $errors[] = $ex->getMessage();
}

Prevention

When it happens

Trigger: Any editor pattern (config `editor` or user preference) whose last character is a single unescaped '%', e.g. 'vim://open?url=%f&rate=100%' - the loop hits $ii+1 beyond the string end and throws before any URI is built.

Common situations: Patterns embedding percent-encoded fragments or literals (a trailing '100%', a URL like '...%25' trimmed wrong); hand-crafted patterns never exercised until a user clicks an editor link; migration scripts concatenating strings that end with '%'.

Related errors


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