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
- Set server.externalUrl to a full URL including protocol, e.g. 'https://your-domain.com'.
- If running locally without a domain, omit externalUrl entirely — the server falls back to constructing origin from host/port config.
- 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
- Always include the protocol (http:// or https://) in externalUrl config.
- Validate externalUrl at deploy time with a URL parser.
- If no external URL is needed, leave externalUrl unset — the server constructs one from host config.
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
- Invalid config for module [${module}] with key [${key}] Valu
- Invalid database index: ${db}, must be between 0 and 11
- action_forbidden
- ErrorCode.ValueNotExists
- action_forbidden
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/a5703b5da36dd53d.
Report an issue: GitHub.