passbolt/passbolt_api · error · BadRequestException
Account recovery case must be a string.
Error message
Account recovery case must be a string.
What it means
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.
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.
Example fix
// before
curl -d '{"username":"u@x.com","case":3}' /recover.json
// after
curl -d '{"username":"u@x.com","case":"lost-token"}' /recover.json Defensive patterns
Strategy: type-guard
Validate before calling
function assertCase(v) {
if (v === undefined || v === null) return 'default';
if (typeof v !== 'string') throw new TypeError('case must be a string');
return v;
}
body.case = assertCase(rawCase); Type guard
const isString = (v) => typeof v === 'string'; const safeCase = isString(input.case) ? input.case : undefined;
Try / catch
try {
await recover({ username, case: safeCase });
} catch (e) {
if (e.response?.status === 400 && e.response.body?.message?.includes('must be a string')) {
console.error('Send "case" as a quoted JSON string, e.g. "lost-token"');
}
} Prevention
- 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.
When it happens
Trigger: POST /recover.json with body like {"username": "a@b.com", "case": 123} or {"case": ["lost-token"]} — any non-string 'case' value.
Common situations: 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.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Account recovery reason not supported.
- Please provide a valid email address.
- Account recovery is disabled.
- An array of arrays is expected.
- An authentication token should be provided.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/620edfb6eaa6a4fe.
Report an issue: GitHub.
Appendix: source
Thrown at src/Service/Users/UserRecoverService.php:137
{
$viewBuilder
->setTemplatePath('Auth')
->setLayout('default')
->setTemplate('triage');
}
/**
* @return string self::ACCOUNT_RECOVERY_CASE_DEFAULT default
*/
protected function assertRecoveryCase(): string
{
$case = $this->request->getData('case') ?? null;
if (!isset($case)) {
return self::ACCOUNT_RECOVERY_CASE_DEFAULT;
}
if (!is_string($case)) {
throw new BadRequestException(__('Account recovery case must be a string.'));
}
if (!in_array($case, self::ACCOUNT_RECOVERY_CASES)) {
throw new BadRequestException(__('Account recovery reason not supported.'));
}
return $case;
}
/**
* Assert some username data is provided
*
* @throws \Cake\Http\Exception\BadRequestException if the username is not valid
* @throws \Cake\Http\Exception\BadRequestException if the username is not provided
* @return string validated username
*/
protected function assertUsername(): string
{
$username = $this->request->getData('username') ?? null;View on GitHub (pinned to 31c1bbc10f)