phalcon/cphalcon · error · Phalcon\Auth\Exceptions\FileDoesNotContainJson

Stream adapter file does not contain a JSON array: {path}

Error message

Stream adapter file does not contain a JSON array: {path}

What it means

The Stream auth adapter (Phalcon\Auth\Adapter\Stream) loads its users from a JSON file that must be a top-level JSON array of user records, e.g. [{"id":1,...},{...}]. This error means the file was read and decoded successfully, but the decoded top-level value is not an array - typically a JSON object like {"users":[...]} or a scalar/string/number. It is thrown from loadUsers() after the Decode helper succeeded but typeof data !== "array".

Source

Thrown at phalcon/Auth/Adapter/Stream.zep:89

        if (!this->phpFileExists(path)) {
            throw new FileDoesNotExist(path);
        }

        let contents = this->phpFileGetContents(path);

        if (contents === false) {
            throw new FileCannotRead(path);
        }

        try {
            let data = (new Decode())->__invoke(contents, true);
        } catch InvalidArgumentException, ex {
            throw new FileNotValidJson(path, ex);
        }

        if (typeof data !== "array") {
            throw new FileDoesNotContainJson(path);
        }

        /** @var list<AuthUserRow> $rows */
        let rows = array_values(data);

        return rows;
    }
}

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Rewrite the users file so the top-level value is a JSON array of user objects: [{"id":1,"email":"a@b","password":"<hash>"}, ...]
  2. If you need a wrapper structure, extract the inner list to its own file used by the adapter, or preprocess the file before pointing the adapter at it
  3. Verify the shape before wiring the adapter: $data = json_decode((string) file_get_contents($path), true); assert is_array($data)

Example fix

// before: storage/users.json
{"users": [{"id":1,"email":"a@b","password":"..."}]}

// after: storage/users.json
[{"id":1,"email":"a@b","password":"..."}]
Defensive patterns

Strategy: validation

Validate before calling

$path = 'storage/users.json';
$data = json_decode((string) file_get_contents($path), true);
if (!is_array($data)) {
    throw new InvalidArgumentException("{$path} must contain a top-level JSON array of users");
}

Type guard

function isTopLevelJsonArray(string $json): bool
{
    return is_array(json_decode($json, true));
}

Try / catch

try {
    $auth->guard('web')->attempt($credentials);
} catch (\Phalcon\Auth\Exceptions\FileDoesNotContainJson $e) {
    // report the file path from the message; treat as configuration error, do not retry
}

Prevention

When it happens

Trigger: Any code path that makes the Stream adapter load users: $auth->attempt(...), $auth->validate(...), guard()->user(), or building the adapter via Stream::fromOptions($hasher, ['file' => $path]) and then authenticating - where $path contains valid JSON whose root is an object or scalar instead of an array.

Common situations: A users.json authored as {"users": [...]} (wrapper object) because that reads more naturally; exporting a single user object {...}; storing a JSON object keyed by user id; hand-editing the file and saving a scalar.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/1ea9cadf1b29d9d9. Report an issue: GitHub.