nextcloud/all-in-one · error · \Exception
Could not extract the API token from the deSEC login respons
Error message
Could not extract the API token from the deSEC login response. Please try again.
What it means
POST /auth/login/ returned 200/201, but the body either failed json_decode (JSON_THROW_ON_ERROR fires and, being a JsonException, escapes the same code path in practice) or the decoded value was not an array containing a string 'token' field. The documented contract {"token": "..."} was violated.
Source
Thrown at php/src/Desec/DesecManager.php:263
]);
} 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-account
* login can point AIO at a domain the user already owns. (deSEC returns 400 when a name
* conflicts with another user's zone and 403 once the per-account domain limit is hit;
* both look like a failure here even though the user owns the name.)
*
* @return string the fully-qualified domain name that was registeredView on GitHub (pinned to 6b788eec5e)
Solutions
- Log the raw login response body and compare it against the deSEC OpenAPI auth/login schema
- Check https://desec.io/ docs for API response-shape changes
- If a proxy interferes, bypass or correctly configure it for desec.io
- Retry once — transient truncation can yield invalid JSON
Defensive patterns
Strategy: retry
Type guard
// Shape check for a deSEC login payload — usable if you call the API directly
function isValidDesecTokenResponse(mixed $data): bool {
return is_array($data) && isset($data['token']) && is_string($data['token']) && $data['token'] !== '';
} Try / catch
try {
$token = $manager->loginAccount($email, $password);
} catch (\Exception $e) {
if (str_contains($e->getMessage(), 'Could not extract the API token')) {
sleep(2);
return $manager->loginAccount($email, $password); // one retry: transient truncation is plausible
}
throw $e;
} Prevention
- Treat a schema drift here as an API-version incident — diff the raw body against the documented {token: string} shape
- Ensure no intermediary proxy can rewrite desec.io responses
- Pin and review deSEC API docs when bumping integration code
When it happens
Trigger: deSEC API change renaming/moving the token field; an HTML page returned by an intermediary proxy behind a 200 status; a truncated response body.
Common situations: Transparent proxy or captive portal rewriting responses; desec.io deploying a new API version; extremely rare malformed payload on the wire.
Related errors
- A domain is already configured. Reset the AIO instance first
- Please provide a valid email address.
- The desired subdomain must contain only lowercase letters, d
- Could not reach the deSEC API: {message}
- Unexpected response from deSEC during account registration (
AI-assisted analysis of nextcloud/all-in-one@6b788eec5e (2026-08-21).
Data as JSON: /api/errors/f975718e06c3f143.
Report an issue: GitHub.