nextcloud/all-in-one · error · \Exception
Unexpected response from deSEC during login (HTTP {code}): {
Error message
Unexpected response from deSEC during login (HTTP {code}): {body} What it means
POST /auth/login/ returned a status other than the accepted 200/201 (and other than the 400/403 invalid-credentials case), so the manager surfaces the unexpected status code plus the raw body. Transport errors cannot produce this — they are caught earlier as 'Could not reach the deSEC API'.
Source
Thrown at php/src/Desec/DesecManager.php:258
*/
public function loginAccount(string $email, string $password): string {
try {
$res = $this->guzzleClient->post($this->configurationManager->desecApiBase . '/auth/login/', [
'json' => ['email' => $email, 'password' => $password],
]);
} catch (TransferException $e) {
throw new \Exception('Could not reach the deSEC API: ' . $e->getMessage());
}
$code = $res->getStatusCode();
$body = $res->getBody()->getContents();
if ($code === 400 || $code === 403) {
throw new \Exception('Could not log in to deSEC: invalid email address or password.');
}
if ($code !== 200 && $code !== 201) {
throw new \Exception('Unexpected response from deSEC during login (HTTP ' . $code . '): ' . $body);
}
$data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($data) || !isset($data['token']) || !is_string($data['token'])) {
throw new \Exception('Could not extract the API token from the deSEC login response. Please try again.');
}
return $data['token'];
}
/**
* Registers a dedyn.io domain for the authenticated account.
* When $slug is empty a random 10-character slug is tried up to MAX_SLUG_ATTEMPTS times.
*
* When a specific slug is requested and creation fails because the name is unavailable
* (HTTP 400/409) or the account's domain limit is reached (HTTP 403), the domain may
* already belong to this very account — a user reusing a slug they registered earlier.
* In that case we reuse the existing domain instead of failing, so an existing-accountView on GitHub (pinned to 6b788eec5e)
Solutions
- Read the embedded body — it carries the daemon/API's own error text
- On 429, wait before retrying instead of looping immediately
- On 5xx, check deSEC service status and retry later
- If a proxy is in path, bypass it for desec.io or configure it correctly
Defensive patterns
Strategy: try-catch
Try / catch
try {
$token = $manager->loginAccount($email, $password);
} catch (\Exception $e) {
if (preg_match('/Unexpected response from deSEC during login \(HTTP (\d+)\)/', $e->getMessage(), $m)) {
[$code, $body] = [$m[1], substr($e->getMessage(), strpos($e->getMessage(), '): ') + 3)];
if ($code === '429') { scheduleRetry(60); return; }
logError("deSEC login anomaly HTTP $code: $body");
}
throw $e;
} Prevention
- Rate-limit your own login attempts so deSEC's 429 is never triggered
- Log the full body on unexpected statuses — it is the only place the API explains itself
- Monitor deSEC API changelog for status-code contract changes
When it happens
Trigger: deSEC returns 429 (login rate limit), 401, 5xx, or an intermediate proxy answers with its own status; any code outside {200, 201, 400, 403}.
Common situations: Repeated login retries hitting deSEC rate limiting; desec.io incident returning 502/503; an intercepting proxy or captive portal responding instead of the API.
Related errors
- Unexpected response from deSEC during account registration (
- Could not log in to deSEC for {email} yet. Two things to che
- Unexpected response from deSEC during domain registration (H
- Unexpected response from deSEC while checking domain ownersh
- A domain is already configured. Reset the AIO instance first
AI-assisted analysis of nextcloud/all-in-one@6b788eec5e (2026-08-21).
Data as JSON: /api/errors/3d9bcf592798fd6a.
Report an issue: GitHub.