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
- Rewrite the users file so the top-level value is a JSON array of user objects: [{"id":1,"email":"a@b","password":"<hash>"}, ...]
- 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
- 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
- Keep users fixtures as top-level arrays and assert the shape in a build/CI step
- Add a smoke check at boot: json_decode the file and is_array it before wiring the Stream adapter
- If multiple shapes exist in your project, write the users file via a generator you control rather than by hand
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
- Stream adapter file is not valid JSON: {path}
- Stream adapter file does not exist: {path}
- Malformed ACL snapshot structure
- Stream adapter cannot read file: {path}
- Session guard 'name' and 'rememberName' must differ
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/1ea9cadf1b29d9d9.
Report an issue: GitHub.