phalcon/cphalcon · error · Phalcon\Auth\Exceptions\DataMustContainIdKey
AuthUser data must contain a scalar 'id' key (int|string)
Error message
AuthUser data must contain a scalar 'id' key (int|string)
What it means
Phalcon\Auth\AuthUser wraps a single user record and requires every record to carry an 'id' key holding a scalar int or string. The constructor validates this invariant and throws DataMustContainIdKey when 'id' is absent (also when it is null, since isset() fails) or when its type is neither int nor string (e.g. array, bool, float). Every adapter that materializes users as AuthUser instances depends on this contract.
Source
Thrown at phalcon/Auth/AuthUser.zep:38
* Lightweight value object returned by array-backed adapters (Memory, Stream)
* when no application model class is configured.
*/
class AuthUser implements AuthUserContract
{
/**
* @phpstan-var array<string, mixed>
*/
protected array data;
/**
* @param array<string, mixed> $data
*
* @throws Exception when $data does not contain a scalar 'id' key.
*/
public function __construct(array data)
{
if (!isset(data["id"]) || (typeof data["id"] !== "int" && typeof data["id"] !== "string")) {
throw new DataMustContainIdKey();
}
let this->data = data;
}
public function getAuthIdentifier() -> int | string
{
var id;
/** @var int|string $id (validated in constructor) */
let id = this->data["id"];
return id;
}
public function getAuthPassword() -> string
{
var password;View on GitHub (pinned to b7419de9cd)
Solutions
- Ensure every user record passed to AuthUser contains a scalar 'id' => int|string before constructing it
- If your PK column is named differently, map it to 'id' when building the row (e.g. $row['id'] = $row['user_id'])
- Guard at the boundary: validate the row shape in your adapter's toUser conversion instead of letting the constructor throw
Example fix
// before $user = new AuthUser($row); // $row has 'user_id', no 'id' // after $row['id'] = $row['user_id']; $user = new AuthUser($row);
Defensive patterns
Strategy: type-guard
Validate before calling
foreach ($rows as $row) {
if (!isset($row['id']) || (!is_int($row['id']) && !is_string($row['id']))) {
throw new InvalidArgumentException('User row lacks scalar int|string id');
}
} Type guard
function hasScalarAuthId(array $row): bool
{
return array_key_exists('id', $row)
&& (is_int($row['id']) || is_string($row['id']));
} Try / catch
try {
$user = new \Phalcon\Auth\AuthUser($row);
} catch (\Phalcon\Auth\Exceptions\DataMustContainIdKey $e) {
// skip or reject the row; log which record was malformed
} Prevention
- Map your primary key to 'id' in a single adapter-level conversion point
- Validate fixture/user exports with the hasScalarAuthId guard before use
- Never store composite/array values under 'id'
When it happens
Trigger: new AuthUser(['email' => 'a@b']) (no 'id'); new AuthUser(['id' => null]); new AuthUser(['id' => ['nested' => 1]]); or an adapter (Stream, model-based) returning rows whose primary key column is named something other than 'id' so the row never contains an 'id' key.
Common situations: Custom auth adapter returning rows keyed by a different PK name (user_id, uuid); a users table where the PK column is renamed but not mapped; a row where id is stored as a composite/array structure; boolean flags accidentally placed under 'id'.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Authenticated user does not implement 'Phalcon\Acl\RoleAware
- Auth {context} requires '{key}' to be a non-empty array
- Auth {context} requires '{key}' to be a non-empty string
- Headers have already been sent; cannot emit the response.
- No route matched the request.
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/f8efcdf9ba668780.
Report an issue: GitHub.