passbolt/passbolt_api · error · InternalErrorException

Invalid response. Expected array, got

Error message

Invalid response. Expected array, got "%s". Response text (truncated): %s

What it means

validateOpenIdConfiguration() requires the discovery document to be an array (decoded JSON object). When the parsed response is not an array (e.g. a raw string of HTML or plain text), it throws InternalErrorException with the expected type, a 200-char truncated excerpt of the response text (UTF-8-safe via mb_strcut, JSON-encoded to protect logs). This guards against IdPs returning error pages instead of the OIDC discovery JSON.

Solutions

  1. Inspect the truncated response excerpt in the message to see what the endpoint actually returned.
  2. Curl the .well-known/openid-configuration URL from the passbolt server and confirm it returns JSON.
  3. Fix the issuer/base URL in passbolt SSO settings to the real OIDC discovery endpoint.
  4. Remove any proxy/rewrite rules intercepting outbound requests to the IdP.

Example fix

// before (issuer points to UI page)
'issuer' => 'https://auth.example.com/console',
// after (points to OIDC discovery root)
'issuer' => 'https://auth.example.com',
Defensive patterns

Strategy: validation

Validate before calling

$body = file_get_contents($wellKnownUrl);
$decoded = json_decode($body, true);
if (!is_array($decoded)) { throw new UnexpectedValueException('Discovery endpoint did not return a JSON object: ' . mb_strcut((string)$body, 0, 200)); }

Type guard

function isDiscoveryDocument(mixed $response): bool { return is_array($response) && isset($response['jwks_uri'], $response['authorization_endpoint'], $response['token_endpoint']); }

Try / catch

try { $config = $provider->getOpenIdConfiguration(); } catch (InternalErrorException $e) { if (str_contains($e->getMessage(), 'Expected array')) { /* IdP returned HTML/error page: check issuer URL and proxy */ } throw $e; }

Prevention

When it happens

Trigger: The GET to the .well-known/openid-configuration endpoint returns a non-JSON body (string): HTML login/proxy page, plain-text error, or a body that failed JSON decoding earlier.

Common situations: Reverse proxy or captive portal intercepting the request; wrong issuer URL pointing at an HTML page; IdP outage returning an HTML error page; misconfigured base URL behind authentication.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/4b80438334ffe624. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Sso/src/Utility/Provider/AbstractOauth2Provider.php:172

    }

    /**
     * Check the endpoints info we expect to use later are present
     *
     * @param mixed $response from .well-known
     * @return void
     */
    public function validateOpenIdConfiguration(mixed $response): void
    {
        if (!is_array($response)) {
            $msg = sprintf('Invalid response. Expected array, got "%s".', gettype($response));
            if (is_string($response)) {
                // Cap excerpt to limit log volume on large/HTML responses; mb_strcut is UTF-8-safe.
                $excerpt = mb_strcut($response, 0, 200, 'UTF-8');
                // Escape newlines and control characters via JSON encoding so they don't corrupt log output.
                $msg .= ' ' . sprintf('Response text (truncated): %s', json_encode($excerpt));
            }
            throw new InternalErrorException($msg);
        }
        if (!isset($response['jwks_uri'])) {
            throw new InternalErrorException('Invalid response. Missing JWKS URI');
        }
        if (!isset($response['authorization_endpoint'])) {
            throw new InternalErrorException('Invalid response. Missing authorization endpoint.');
        }
        if (!isset($response['token_endpoint'])) {
            throw new InternalErrorException('Invalid response. Missing token endpoint.');
        }
        if (!Validation::url($response['jwks_uri'])) {
            throw new InternalErrorException('Invalid response. Invalid JWKS URI');
        }
        if (!Validation::url($response['authorization_endpoint'])) {
            throw new InternalErrorException('Invalid response. Invalid authorization endpoint.');
        }
        if (!Validation::url($response['token_endpoint'])) {
            throw new InternalErrorException('Invalid response. Invalid token endpoint.');

View on GitHub (pinned to 31c1bbc10f)