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

  1. Pass a fallback user agent when the header is missing: $request->getHeaderLine('User-Agent') ?: 'unknown' before constructing the object.
  2. In CLI/background jobs, pass a descriptive literal such as 'passbolt-cli' or 'passbolt-recovery' as $userAgent.
  3. Check the UserAgentValidation rules and ensure the string does not exceed the configured max length or contain control characters; sanitize the header value first.
  4. 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

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


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 string

View on GitHub (pinned to 31c1bbc10f)