phacility/phabricator · error · PhabricatorDocumentEngineParserException

Failed to "json_decode(...)" JSON document after successfull

Error message

Failed to "json_decode(...)" JSON document after successfully decoding it with "phutil_json_decode(...).

What it means

PhabricatorJSONDocumentEngine decodes twice: phutil_json_decode() first validates the JSON, then json_decode($raw_data, false) re-decodes it to preserve the object ('{}') vs list ('[]') distinction for rendering. This exception fires when the second decode yields a falsy PHP value — which happens for valid JSON whose root is a falsy literal: null, 0, false, "" or []. In other words it is a false-positive: the truthiness check (!$data) mistakes a valid falsy decode for a decoding failure.

Source

Thrown at src/applications/files/document/PhabricatorJSONDocumentEngine.php:43

    if ($ref->isProbablyJSON()) {
      return 1750;
    }

    return 500;
  }

  protected function newDocumentContent(PhabricatorDocumentRef $ref) {
    $raw_data = $this->loadTextData($ref);

    try {
      $data = phutil_json_decode($raw_data);

      // See T13635. "phutil_json_decode()" always turns JSON into a PHP array,
      // and we lose the distinction between "{}" and "[]". This distinction is
      // important when rendering a document.
      $data = json_decode($raw_data, false);
      if (!$data) {
        throw new PhabricatorDocumentEngineParserException(
          pht(
            'Failed to "json_decode(...)" JSON document after successfully '.
            'decoding it with "phutil_json_decode(...).'));
      }

      if (preg_match('/^\s*\[/', $raw_data)) {
        $content = id(new PhutilJSON())->encodeAsList($data);
      } else {
        $content = id(new PhutilJSON())->encodeFormatted($data);
      }

      $message = null;
      $content = PhabricatorSyntaxHighlighter::highlightWithLanguage(
        'json',
        $content);
    } catch (PhutilJSONParserException $ex) {
      $message = $this->newMessage(
        pht(

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Work around by giving the document a non-empty root: use an object with at least one member instead of a bare 'null', '0', or '[]'
  2. Patch the engine to detect failure with a strict null check plus json_last_error() instead of truthiness (the upstream class of fix)
  3. If rendering keeps failing, view/download the raw file instead of the formatted JSON view

Example fix

// before
$data = json_decode($raw_data, false);
if (!$data) {
  throw new PhabricatorDocumentEngineParserException(
    pht('Failed to "json_decode(...)" JSON document ...'));
}

// after
$data = json_decode($raw_data, false);
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
  throw new PhabricatorDocumentEngineParserException(
    pht('Failed to "json_decode(...)" JSON document ...'));
}
Defensive patterns

Strategy: try-catch

Validate before calling

$decoded = json_decode($raw, false);
$valid = ($decoded !== null || json_last_error() === JSON_ERROR_NONE);
if (!$valid) {
  // do not hand the document to the JSON engine
}

Try / catch

catch PhabricatorDocumentEngineParserException specifically and fall back to plain-text rendering of the raw bytes; do not retry — the outcome is deterministic for a given document.

Prevention

When it happens

Trigger: Uploading a .json file whose entire content is 'null', '0', 'false', '""' or '[]'. Each is valid JSON, so phutil_json_decode() succeeds, but json_decode() returns null/0/''/empty array and the if (!$data) guard throws.

Common situations: Machine-generated artifacts that legitimately contain a bare scalar or empty list (e.g. '[]' from a linter with no findings, 'null' from an API dump); minimal hand-written JSON test files.

Related errors


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