{"record":{"id":"bcb2a3eabbefd1f2","repo":"passbolt/passbolt_api","slug":"this-is-not-a-valid-ajax-json-request-recoverstartcontroller","errorCode":null,"errorMessage":"This is not a valid Ajax/Json request.","messagePattern":"This is not a valid Ajax/Json request\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/SsoRecover/src/Controller/RecoverStartController.php","lineNumber":53,"sourceCode":"    /**\n     * @inheritDoc\n     */\n    public function beforeFilter(EventInterface $event)\n    {\n        parent::beforeFilter($event);\n\n        $this->Authentication->allowUnauthenticated(['start']);\n    }\n\n    /**\n     * Exchange authentication token for recover URL.\n     *\n     * @return void\n     */\n    public function start(): void\n    {\n        if (!$this->request->is('json')) {\n            throw new BadRequestException(__('This is not a valid Ajax/Json request.'));\n        }\n\n        $this->User->assertNotLoggedIn();\n\n        // Make sure SSO settings are set.\n        try {\n            $settingsDto = (new SsoSettingsGetService())->getActiveOrFail();\n        } catch (RecordNotFoundException $e) {\n            throw new BadRequestException(__('No valid SSO settings found.'), null, $e);\n        }\n\n        $form = new SsoRecoverStartForm();\n        if (!$form->execute($this->getRequest()->getData())) {\n            throw new FormValidationException(__('Could not validate the SSO recover request.'), $form);\n        }\n\n        // Assert & consume sso auth token\n        $ssoAuthService = new SsoAuthenticationTokenGetService();","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/SsoRecover/src/Controller/RecoverStartController.php#L35-L71","documentation":"Thrown by RecoverStartController::start when the request is NOT a JSON/Ajax request. Unlike the success endpoints, the recover-start endpoint is part of the programmatic API and requires the JSON request marker (Accept: application/json with X-Requested-With per CakePHP/Passbolt conventions).","triggerScenarios":"Calling POST /sso/recover/start from a plain HTML form, curl without JSON headers, or a browser address-bar navigation; missing Accept: application/json header.","commonSituations":"API consumers forget the JSON headers passbolt requires on all /json endpoints; testing in a browser; proxy strips Accept headers.","solutions":["Send headers Accept: application/json and X-Requested-With: XMLHttpRequest with the request","Use the passbolt JS SDK or an HTTP client configured for JSON when calling this endpoint","Ensure Content-Type: application/json when posting the form data","Do not open the endpoint directly in a browser address bar for POST flows"],"exampleFix":"// before\ncurl -X POST https://host/sso/recover/start -d 'username=...'\n// after\ncurl -X POST https://host/sso/recover/start \\\n  -H 'Accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -H 'X-Requested-With: XMLHttpRequest' \\\n  -d '{\"username\":\"...\"}'","handlingStrategy":"validation","validationCode":"const jsonHeaders = {\n  'Accept': 'application/json',\n  'Content-Type': 'application/json',\n  'X-Requested-With': 'XMLHttpRequest'\n};\nawait fetch('/sso/recover/start', {method: 'POST', headers: jsonHeaders, body});","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always send JSON headers on passbolt /json API endpoints","Use the official passbolt SDK/client wrappers","Never invoke POST endpoints via browser address bar","Check that proxies/CDNs do not strip Accept headers"],"tags":["sso","http","content-negotiation","json"],"backgroundTag":"operation-not-supported","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}