passbolt/passbolt_api · error · InternalErrorException
Failed extended user control. Invalid IP Address.
Error message
Failed extended user control. Invalid IP Address.
What it means
The ExtendedUserAccessControl constructor validates the optional $userIp argument with CakePHP's Validation::ip() and throws an InternalErrorException when it is not a valid IPv4/IPv6 address. This immutable value object wraps user identity plus request metadata, so an invalid IP means the caller passed missing, empty, or malformed input (Validation::ip() also rejects null). The exception is thrown before the object is ever constructed, so no partially-initialized instance leaks.
Solutions
- Ensure a valid IP string is passed: read it from $request->clientIp() (or getServerParams()['REMOTE_ADDR']) and default to a safe value like '127.0.0.1' when the request has no IP (CLI context).
- If behind a proxy, configure passbolt to trust the proxy and pass the first entry of X-Forwarded-For, trimmed and validated before constructing the object.
- Guard before construction: if ($userIp === null || !Validation::ip($userIp)) { $userIp = '127.0.0.1'; } for CLI or header-less contexts.
- Catch InternalErrorException around construction in controllers/services and convert it to a 400-level response instead of a 500.
Example fix
// before
$uac = new ExtendedUserAccessControl(
Role::USER,
$user->id,
$user->username,
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null
);
// after
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
$ip = trim(explode(',', $ip)[0]);
if (!Validation::ip($ip)) {
$ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
}
$uac = new ExtendedUserAccessControl(
Role::USER,
$user->id,
$user->username,
$ip,
$_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
); Defensive patterns
Strategy: validation
Validate before calling
use Cake\Validation\Validation;
$ip = trim(explode(',', (string)($request->getHeaderLine('X-Forwarded-For') ?: $_SERVER['REMOTE_ADDR'] ?? '')))[0] ?? '';
if (!Validation::ip($ip)) {
$ip = '127.0.0.1'; // safe fallback for CLI/header-less requests
}
Type guard
/** @param mixed $ip @phpstan-assert non-empty-string $ip */
function isValidIp(mixed $ip): bool
{
return is_string($ip) && \Cake\Validation\Validation::ip($ip);
}
Try / catch
try {
$uac = new ExtendedUserAccessControl($role, $userId, $username, $ip, $ua);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
// log $e->getMessage(), fall back to a 4xx client error response
}
Prevention
- Always source the IP via $request->clientIp() rather than raw headers.
- Default to 127.0.0.1 in CLI/background contexts where no request IP exists.
- Trim and take the first element of comma-separated X-Forwarded-For values.
- Unit-test the constructor with null, empty, and proxy-style IP inputs.
When it happens
Trigger: Calling new ExtendedUserAccessControl($role, $userId, $username, $userIp, $userAgent) with $userIp = null, an empty string, a hostname, an IPv6 with bad syntax, or an IP read from a spoofed/absent header like Client-Ip or X-Forwarded-For that contains garbage or a comma-separated list.
Common situations: Running behind a reverse proxy or load balancer where REMOTE_ADDR is empty or the forwarded header is missing/malformed; CLI commands (recovery, emails queue) constructing the object without request context so no IP exists; integration tests sending requests without REMOTE_ADDR; proxies appending multiple IPs ('1.2.3.4, 10.0.0.1') which fails Validation::ip().
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Failed extended user control. Invalid user agent.
- " " is not a valid search filter.
- " " is not a valid search filter. It is not a UTF8 string.
- " " is not a valid search filter. It should be between 1…
- " " is not a valid user filter.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/c1fd53b5c9610a47.
Report an issue: GitHub.
Appendix: source
Thrown at src/Utility/ExtendedUserAccessControl.php:62
* UserAccessControl constructor.
*
* @param string $roleName The role name
* @param string|null $userId the user uuid
* @param string|null $username the user email
* @param string|null $userIp the user ip
* @param string|null $userAgent the user agent
*/
public function __construct(
string $roleName,
?string $userId = null,
?string $username = null,
?string $userIp = null,
?string $userAgent = null
) {
parent::__construct($roleName, $userId, $username);
if (!Validation::ip($userIp)) {
throw new InternalErrorException('Failed extended user control. Invalid IP Address.');
}
$this->userIp = $userIp;
if (!UserAgentValidation::isValid($userAgent)) {
throw new InternalErrorException('Failed extended user control. Invalid user agent.');
}
$this->userAgent = $userAgent;
}
/**
* Get the user ip address
*
* @return string
*/
public function getUserIp(): string
{
return $this->userIp;
}View on GitHub (pinned to 31c1bbc10f)