{"record":{"id":"a5703b5da36dd53d","repo":"toeverything/AFFiNE","slug":"invalid-server-externalurl-configured-it-must-b","errorCode":null,"errorMessage":"Invalid `server.externalUrl` configured. It must be a valid url.","messagePattern":"Invalid `server\\.externalUrl` configured\\. It must be a valid url\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/backend/server/src/base/helpers/url.ts","lineNumber":53,"sourceCode":"  redirectAllowHosts!: string[];\n\n  origin!: string;\n  allowedOrigins!: string[];\n  baseUrl!: string;\n\n  constructor(\n    private readonly config: Config,\n    private readonly cls?: ClsService\n  ) {\n    this.init();\n  }\n\n  @OnEvent('config.changed')\n  @OnEvent('config.init')\n  init() {\n    if (this.config.server.externalUrl) {\n      if (!this.verify(this.config.server.externalUrl)) {\n        throw new Error(\n          'Invalid `server.externalUrl` configured. It must be a valid url.'\n        );\n      }\n\n      const externalUrl = new URL(this.config.server.externalUrl);\n\n      this.origin = externalUrl.origin;\n      this.baseUrl =\n        externalUrl.origin + externalUrl.pathname.replace(/\\/$/, '');\n    } else {\n      this.origin = this.convertHostToOrigin(this.config.server.host);\n      this.baseUrl = this.origin + this.config.server.path;\n    }\n\n    this.redirectAllowHosts = [this.baseUrl];\n\n    this.allowedOrigins = [this.origin];\n    if (this.config.server.hosts.length > 0) {","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/base/helpers/url.ts#L35-L71","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (.env)\nSERVER_EXTERNAL_URL=\"my-app.com\"\n\n// after\nSERVER_EXTERNAL_URL=\"https://my-app.com\"","handlingStrategy":"validation","validationCode":"function isValidExternalUrl(url: string): boolean {\n  try {\n    const u = new URL(url);\n    return ['http:', 'https:'].includes(u.protocol) && !!u.hostname;\n  } catch {\n    return false;\n  }\n}\nconst externalUrl = process.env.AFFINE_SERVER_EXTERNAL_URL;\nif (externalUrl && !isValidExternalUrl(externalUrl)) {\n  throw new Error('AFFINE_SERVER_EXTERNAL_URL must be a valid http(s) URL');\n}","typeGuard":"const isValidUrl = (url: string): boolean => {\n  try {\n    const u = new URL(url);\n    return ['http:', 'https:'].includes(u.protocol) && !!u.hostname;\n  } catch {\n    return false;\n  }\n};","tryCatchPattern":null,"preventionTips":["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."],"tags":["affine-backend","config","url","startup","server"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}