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
- Inspect the truncated response excerpt in the message to see what the endpoint actually returned.
- Curl the .well-known/openid-configuration URL from the passbolt server and confirm it returns JSON.
- Fix the issuer/base URL in passbolt SSO settings to the real OIDC discovery endpoint.
- 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
- Verify the discovery URL returns application/json (curl -I)
- Exclude the IdP domain from captive-portal/auth proxies
- Point the issuer at the OIDC root, not an HTML console page
- Add a health check that decodes the discovery document on deploy
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
- $exception->getMessage() (dynamic wrapped error)
- Invalid response. Invalid authorization endpoint.
- Invalid response. Invalid JWKS URI
- Invalid response. Invalid token endpoint.
- Invalid response. Missing authorization endpoint.
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)