toeverything/AFFiNE · critical

Invalid `server.externalUrl` configured. It must be a valid

Error message

Invalid `server.externalUrl` configured. It must be a valid url.

What it means

Thrown by URLHelper.init() (triggered on 'config.init' and 'config.changed' events) when the configured server.externalUrl fails the verify() check — meaning it's not a parseable URL with an http/https protocol and a hostname. The externalUrl is critical for generating links, redirects, and CORS origins, so an invalid value is fatal.

Source

Thrown at packages/backend/server/src/base/helpers/url.ts:53

  redirectAllowHosts!: string[];

  origin!: string;
  allowedOrigins!: string[];
  baseUrl!: string;

  constructor(
    private readonly config: Config,
    private readonly cls?: ClsService
  ) {
    this.init();
  }

  @OnEvent('config.changed')
  @OnEvent('config.init')
  init() {
    if (this.config.server.externalUrl) {
      if (!this.verify(this.config.server.externalUrl)) {
        throw new Error(
          'Invalid `server.externalUrl` configured. It must be a valid url.'
        );
      }

      const externalUrl = new URL(this.config.server.externalUrl);

      this.origin = externalUrl.origin;
      this.baseUrl =
        externalUrl.origin + externalUrl.pathname.replace(/\/$/, '');
    } else {
      this.origin = this.convertHostToOrigin(this.config.server.host);
      this.baseUrl = this.origin + this.config.server.path;
    }

    this.redirectAllowHosts = [this.baseUrl];

    this.allowedOrigins = [this.origin];
    if (this.config.server.hosts.length > 0) {

View on GitHub (pinned to 26c515e050)

Solutions

  1. Set server.externalUrl to a full URL including protocol, e.g. 'https://your-domain.com'.
  2. If running locally without a domain, omit externalUrl entirely — the server falls back to constructing origin from host/port config.
  3. Verify the value with new URL(value) in a Node REPL to confirm it parses with http/https protocol and a hostname.

Example fix

// before (.env)
SERVER_EXTERNAL_URL="my-app.com"

// after
SERVER_EXTERNAL_URL="https://my-app.com"
Defensive patterns

Strategy: validation

Validate before calling

function isValidExternalUrl(url: string): boolean {
  try {
    const u = new URL(url);
    return ['http:', 'https:'].includes(u.protocol) && !!u.hostname;
  } catch {
    return false;
  }
}
const externalUrl = process.env.AFFINE_SERVER_EXTERNAL_URL;
if (externalUrl && !isValidExternalUrl(externalUrl)) {
  throw new Error('AFFINE_SERVER_EXTERNAL_URL must be a valid http(s) URL');
}

Type guard

const isValidUrl = (url: string): boolean => {
  try {
    const u = new URL(url);
    return ['http:', 'https:'].includes(u.protocol) && !!u.hostname;
  } catch {
    return false;
  }
};

Prevention

When it happens

Trigger: Server startup or config reload when config.server.externalUrl is set to a value that isn't a valid http(s) URL. The verify() method tries new URL(url), checks protocol is 'http:' or 'https:', and checks hostname is non-empty. Failing any of these throws.

Common situations: Setting AFFINE_SERVER_EXTERNAL_URL env var to a value without a protocol (e.g. 'example.com' instead of 'https://example.com'). Typo including spaces or invalid characters. Config reload via runtime config update with a malformed URL. Forgetting the https:// prefix behind a reverse proxy.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/a5703b5da36dd53d. Report an issue: GitHub.