nextcloud/all-in-one · warning · \Exception

Could not register a free dedyn.io domain after {maxSlugAtte

Error message

Could not register a free dedyn.io domain after {maxSlugAttempts} attempts. Please try again.

What it means

Random-slug mode tried MAX_SLUG_ATTEMPTS (5) times to register a randomly generated 10-hex-character <slug>.dedyn.io name and every attempt returned 409 (collision), because collisions are the only outcome that `continue`s the loop. The exception fires after the fifth consecutive collision.

Source

Thrown at php/src/Desec/DesecManager.php:331

                if ($code === 403) {
                    throw new \Exception(
                        'Your deSEC account has reached its domain limit and "' . $domain . '" is not '
                        . 'one of your existing domains. Remove an unused domain at desec.io, or contact '
                        . 'deSEC support to raise the limit, then try again.'
                    );
                }
                throw new \Exception('"' . $domain . '" is already taken. Please choose a different subdomain and try again.');
            }

            if ($code === 409) {
                // Random slug collided with an existing name — try another.
                continue;
            }

            throw new \Exception('Unexpected response from deSEC during domain registration (HTTP ' . $code . '): ' . $res->getBody()->getContents());
        }

        throw new \Exception('Could not register a free dedyn.io domain after ' . self::MAX_SLUG_ATTEMPTS . ' attempts. Please try again.');
    }

    /**
     * Checks whether the authenticated account already owns the given domain.
     *
     * Used to recover from a failed creation when the user is reusing a slug they
     * registered earlier: GET /domains/{name}/ returns 200 only for a domain the
     * token's account owns, 404 otherwise.
     *
     * @throws \Exception on network failure or an unexpected HTTP response
     */
    private function ownsDomain(string $token, string $domain): bool {
        try {
            $res = $this->guzzleClient->get($this->configurationManager->desecApiBase . '/domains/' . $domain . '/', [
                'headers' => ['Authorization' => 'Token ' . $token],
            ]);
        } catch (TransferException $e) {
            throw new \Exception('Could not reach the deSEC API: ' . $e->getMessage());

View on GitHub (pinned to 6b788eec5e)

Solutions

  1. Simply retry — fresh random slugs are drawn each run
  2. If it recurs, verify desecApiBase and that requests actually reach desec.io (a proxy answering 409 for everything would explain it)
  3. Fall back to a user-chosen unique slug to bypass random generation
Defensive patterns

Strategy: retry

Try / catch

try {
    $domain = $manager->registerDomain($token, ''); // '' = random slug
} catch (\Exception $e) {
    if (str_contains($e->getMessage(), 'Could not register a free dedyn.io domain after')) {
        sleep(1);
        return $manager->registerDomain($token, ''); // fresh random slugs next round
    }
    throw $e;
}

Prevention

When it happens

Trigger: Five consecutive 409s on random bin2hex(random_bytes(5)) names — each attempt retries with a fresh random slug, so reaching this requires astronomically bad luck or a deSEC-side anomaly that answers every POST /domains/ with 409.

Common situations: Practically never in normal operation; if seen, suspect the API is misbehaving (e.g. proxy or misrouted desecApiBase returning 409 for everything) rather than genuine collisions.

Related errors


AI-assisted analysis of nextcloud/all-in-one@6b788eec5e (2026-08-21). Data as JSON: /api/errors/1ef7b85e65bb391e. Report an issue: GitHub.