{"record":{"id":"620edfb6eaa6a4fe","repo":"passbolt/passbolt_api","slug":"account-recovery-case-must-be-a-string","errorCode":null,"errorMessage":"Account recovery case must be a string.","messagePattern":"Account recovery case must be a string\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Service/Users/UserRecoverService.php","lineNumber":137,"sourceCode":"    {\n        $viewBuilder\n            ->setTemplatePath('Auth')\n            ->setLayout('default')\n            ->setTemplate('triage');\n    }\n\n    /**\n     * @return string self::ACCOUNT_RECOVERY_CASE_DEFAULT default\n     */\n    protected function assertRecoveryCase(): string\n    {\n        $case = $this->request->getData('case') ?? null;\n\n        if (!isset($case)) {\n            return self::ACCOUNT_RECOVERY_CASE_DEFAULT;\n        }\n        if (!is_string($case)) {\n            throw new BadRequestException(__('Account recovery case must be a string.'));\n        }\n        if (!in_array($case, self::ACCOUNT_RECOVERY_CASES)) {\n            throw new BadRequestException(__('Account recovery reason not supported.'));\n        }\n\n        return $case;\n    }\n\n    /**\n     * Assert some username data is provided\n     *\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the username is not valid\n     * @throws \\Cake\\Http\\Exception\\BadRequestException if the username is not provided\n     * @return string validated username\n     */\n    protected function assertUsername(): string\n    {\n        $username = $this->request->getData('username') ?? null;","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Service/Users/UserRecoverService.php#L119-L155","documentation":"Thrown by UserRecoverService::assertRecoveryCase when the 'case' field in the request body is present but is not a JSON string (e.g. a number, boolean, array or object). The recovery endpoint accepts only the whitelisted string cases (default, lost-token, etc.); a non-string payload is rejected as a bad request before the enum check.","triggerScenarios":"POST /recover.json with body like {\"username\": \"a@b.com\", \"case\": 123} or {\"case\": [\"lost-token\"]} — any non-string 'case' value.","commonSituations":"Client SDKs or scripts building the JSON body with the wrong type; YAML/JSON config being parsed where a quoted value loses its string type (e.g. case: 1 instead of case: '1'); copy-pasted curl examples missing quotes.","solutions":["Send 'case' as a quoted string in the JSON body: \"case\": \"lost-token\".","Use only supported values from ACCOUNT_RECOVERY_CASES (see UserRecoverService) — anything else triggers the related 'reason not supported' error.","Omit 'case' entirely to get the default case if you do not need a specific one.","Check your client code/curl payload for accidental numeric or nested values."],"exampleFix":"// before\ncurl -d '{\"username\":\"u@x.com\",\"case\":3}' /recover.json\n\n// after\ncurl -d '{\"username\":\"u@x.com\",\"case\":\"lost-token\"}' /recover.json","handlingStrategy":"type-guard","validationCode":"function assertCase(v) {\n  if (v === undefined || v === null) return 'default';\n  if (typeof v !== 'string') throw new TypeError('case must be a string');\n  return v;\n}\nbody.case = assertCase(rawCase);","typeGuard":"const isString = (v) => typeof v === 'string';\nconst safeCase = isString(input.case) ? input.case : undefined;","tryCatchPattern":"try {\n  await recover({ username, case: safeCase });\n} catch (e) {\n  if (e.response?.status === 400 && e.response.body?.message?.includes('must be a string')) {\n    console.error('Send \"case\" as a quoted JSON string, e.g. \"lost-token\"');\n  }\n}","preventionTips":["Serialize request bodies with a JSON library, never manual string interpolation.","Quote string values in curl/YAML examples so they don't degrade to numbers.","Restrict the case field to your app's whitelist of known case strings."],"tags":["passbolt","account-recovery","bad-request","input-validation"],"backgroundTag":"type-mismatch","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"}