passbolt/passbolt_api · error · InternalErrorException
Failed extended user control. Invalid user agent.
Error message
Failed extended user control. Invalid user agent.
What it means
The ExtendedUserAccessControl constructor validates the optional $userAgent argument with App\Utility\Validation\UserAgentValidation::isValid() and throws an InternalErrorException when the string is empty or fails the user-agent format rules. User agents are stored/logged for security events, so blank or junk values are rejected at construction time. Like the IP check, this happens before the object exists.
Solutions
- Pass a fallback user agent when the header is missing: $request->getHeaderLine('User-Agent') ?: 'unknown' before constructing the object.
- In CLI/background jobs, pass a descriptive literal such as 'passbolt-cli' or 'passbolt-recovery' as $userAgent.
- Check the UserAgentValidation rules and ensure the string does not exceed the configured max length or contain control characters; sanitize the header value first.
- If a legitimate client is rejected, inspect what UserAgentValidation::isValid() requires (non-empty, printable, length-bounded) and fix the client to send a conformant User-Agent header.
Example fix
// before
$uac = new ExtendedUserAccessControl(
Role::USER,
$user->id,
$user->username,
$ip,
$request->getHeaderLine('User-Agent') // may be ''
);
// after
$userAgent = $request->getHeaderLine('User-Agent');
if (!UserAgentValidation::isValid($userAgent)) {
$userAgent = 'unknown';
}
$uac = new ExtendedUserAccessControl(
Role::USER,
$user->id,
$user->username,
$ip,
$userAgent
); Defensive patterns
Strategy: validation
Validate before calling
use App\Utility\Validation\UserAgentValidation;
$ua = $request->getHeaderLine('User-Agent');
if (!UserAgentValidation::isValid($ua)) {
$ua = 'unknown';
}
Type guard
/** @param mixed $ua @phpstan-assert non-empty-string $ua */
function isValidUserAgent(mixed $ua): bool
{
return is_string($ua) && UserAgentValidation::isValid($ua);
}
Try / catch
try {
$uac = new ExtendedUserAccessControl($role, $userId, $username, $ip, $ua);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
// log and retry construction with 'unknown' user agent
}
Prevention
- Always pass a non-empty fallback user agent when the header is absent.
- In CLI jobs, pass a literal identifier like 'passbolt-cli' instead of null.
- Strip control characters and cap the UA length before constructing the object.
- Keep curl/API clients configured with an explicit User-Agent header.
When it happens
Trigger: Constructing ExtendedUserAccessControl with $userAgent = null or '' because the HTTP request carried no User-Agent header (bots, curl without -A, CLI jobs), or a user agent string exceeding the expected length or containing stripped/encoded characters that UserAgentValidation::isValid() rejects.
Common situations: API clients or scripts calling passbolt endpoints with curl/requests without setting a User-Agent header; health checks and load balancer probes omitting the header; background jobs constructing the UAC object outside a web request; extremely long or binary-garbage UA strings from malicious clients.
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 IP Address.
- " " 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/4549dac162ab5326.
Report an issue: GitHub.
Appendix: source
Thrown at src/Utility/ExtendedUserAccessControl.php:67
* @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;
}
/**
* Get the user agent
*
* @return stringView on GitHub (pinned to 31c1bbc10f)