passbolt/passbolt_api · error · BadRequestException
$exception->getMessage() from SsoSettingsGetService draft…
Error message
$exception->getMessage() from SsoSettingsGetService draft failure (dynamic), remapped to 400
What it means
In stage2AsAdmin, fetching the draft SSO settings via SsoSettingsGetService::getDraftByIdOrFail() can fail (draft missing, deleted, or wrong status). The controller catches any Exception and remaps its dynamic message to a 400 BadRequestException, chaining the original exception.
Solutions
- Re-open the SSO settings admin screen and restart the provider setup to generate a fresh draft before completing stage2.
- Check the sso_settings record in the database for the id in the state — confirm it exists with status DRAFT.
- Verify no concurrent admin session deleted/modified the settings draft (check audit/logs).
- Ensure the SSO state cookie and the settings draft belong to the same setup attempt; clear cookies and retry.
Defensive patterns
Strategy: try-catch
Validate before calling
const settings = await api.get('/sso/settings/' + ssoState.sso_settings_id + '.json');
if (!settings || settings.status !== 'draft') { throw new Error('SSO settings draft missing; restart setup'); } Type guard
function hasValidDraft(state) { return state != null && typeof state.sso_settings_id === 'string' && state.sso_settings_id.length > 0; } Try / catch
try { await stage2AsAdmin(state, code); } catch (e) { if (e.status === 400) { showAlert('SSO draft settings unavailable: ' + e.message + '. Restart provider setup.'); } } Prevention
- Complete the SSO setup in one session without long pauses
- Avoid two admins editing the same SSO settings concurrently
- Check the draft still exists before finishing stage2 after long delays
When it happens
Trigger: Admin completes SSO provider callback (stage2) but the referenced sso_settings_id in the state no longer points to a valid settings draft, e.g. the draft was saved/deleted or expired between stage1 redirect and stage2 callback.
Common situations: Two admins editing the same SSO settings concurrently — one saves/deletes the draft while the other's callback is in flight; session left open so long the draft was discarded; clicking an old bookmarked callback link.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- $e->getMessage() from OAuth2Exception during admin SSO…
- Ajax/Json request not supported.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
- Ajax/Json request not supported.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/65b31d25e369305b.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltEe/Sso/src/Controller/AbstractSso2Stage2Controller.php:218
}
$this->response = $this->getResponse()->withCookie($service->clearStateCookie());
$this->redirect($successUrl);
}
/**
* @param \App\Service\Cookie\AbstractSecureCookieService $cookieService Cookie service
* @param \Passbolt\Sso\Model\Entity\SsoState $ssoState SSO state.
* @param string $code jwt
* @return void
*/
protected function stage2AsAdmin(AbstractSecureCookieService $cookieService, SsoState $ssoState, string $code): void
{
try {
// Get the draft settings
$settingsDto = (new SsoSettingsGetService())->getDraftByIdOrFail($ssoState->sso_settings_id, true);
} catch (Exception $exception) {
throw new BadRequestException($exception->getMessage(), 400, $exception);
}
try {
$service = $this->ssoServiceFactory($cookieService, $settingsDto);
$uac = $service->assertStateCodeAndGetUac($ssoState, $code, $this->User->ip(), $this->User->userAgent());
} catch (OAuth2Exception $e) { // Remap 500 error with 400 when admin is setting up SSO
throw new BadRequestException($e->getMessage(), 400, $e);
}
// Create authentication token for next step, e.g. activate settings
$ssoAuthToken = $service->createAuthTokenToActiveSettings($uac, $service->getSettings()->id);
$this->response = $this->getResponse()->withCookie($service->clearStateCookie());
$this->redirect(Router::url("/sso/login/dry-run/success?token={$ssoAuthToken->token}", true));
}
}
View on GitHub (pinned to 31c1bbc10f)