coder/code-server · error · Error

Failed to resolve external URI: ${uri.toString()}. Could not

Error message

Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.

What it means

Thrown by the resolveExternalUri implementation added in proxy-uri.diff when the URI to resolve is a localhost URI on a different authority than location.host AND config.productConfiguration or config.productConfiguration.proxyEndpointTemplate is missing. Without the template the code cannot construct the proxied URL for the external port, so it aborts rather than silently returning a broken link.

Source

Thrown at patches/proxy-uri.diff:121

+import { extractLocalHostUriMetaDataForPortMapping, TunnelOptions, TunnelCreationOptions } from '../../../platform/tunnel/common/tunnel.js';
 import type { IURLCallbackProvider } from '../../../workbench/services/url/browser/urlService.js';
 import { create } from '../../../workbench/workbench.web.main.internal.js';
 
@@ -612,6 +613,39 @@ class WorkspaceProvider implements IWork
 		settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined,
 		workspaceProvider: WorkspaceProvider.create(config),
 		urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute),
+		resolveExternalUri: (uri: URI): Promise<URI> => {
+			let resolvedUri = uri
+			const localhostMatch = extractLocalHostUriMetaDataForPortMapping(resolvedUri)
+			if (localhostMatch && resolvedUri.authority !== location.host) {
+				if (config.productConfiguration && config.productConfiguration.proxyEndpointTemplate) {
+					const renderedTemplate = config.productConfiguration.proxyEndpointTemplate
+						.replace('{{port}}', localhostMatch.port.toString())
+						.replace('{{host}}', window.location.host)
+					resolvedUri = URI.parse(new URL(renderedTemplate, window.location.href).toString())
+				} else {
+					throw new Error(`Failed to resolve external URI: ${uri.toString()}. Could not determine base url because productConfiguration missing.`)
+				}
+			}
+			// If not localhost, return unmodified.
+			return Promise.resolve(resolvedUri)
+		},
+		tunnelProvider: {
+			tunnelFactory: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => {
+				const onDidDispose: Emitter<void> = new Emitter();
+				let isDisposed = false;
+				return Promise.resolve({
+					remoteAddress: tunnelOptions.remoteAddress,
+					localAddress: `localhost:${tunnelOptions.remoteAddress.port}`,
+					onDidDispose: onDidDispose.event,
+					dispose: () => {
+						if (!isDisposed) {
+							isDisposed = true;
+							onDidDispose.fire();
+						}

View on GitHub (pinned to 51f90a376b)

Solutions

  1. Provide productConfiguration.proxyEndpointTemplate in the workbench config (e.g. a template like '/proxy/{{port}}/{{host}}').
  2. Ensure code-server's build injects the product configuration including the proxy endpoint template into the browser.
  3. If port proxying is disabled, prevent extensions from calling resolveExternalUri for localhost ports (disable forwarded-port open behavior).
  4. Verify the template contains both {{port}} and {{host}} placeholders.

Example fix

// In the workbench construction options sent to the browser:
config.productConfiguration = {
  ...config.productConfiguration,
  proxyEndpointTemplate: '/proxy/{{port}}/{{host}}',
}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the proxy template is present before resolveExternalUri is reachable.
if (!config.productConfiguration?.proxyEndpointTemplate) {
  throw new Error('proxyEndpointTemplate must be configured for port proxying')
}

Type guard

function hasProxyTemplate(c: any): boolean {
  return !!c?.productConfiguration?.proxyEndpointTemplate
    && c.productConfiguration.proxyEndpointTemplate.includes('{{port}}')
}

Try / catch

try {
  return await resolveExternalUri(uri)
} catch (e) {
  if (/productConfiguration missing/.test(e.message)) return uri // fall back to unmodified URI
}

Prevention

When it happens

Trigger: An extension or workbench feature calls resolveExternalUri on a localhost:<port> URI whose authority differs from the current page host (a port-forwarding scenario), but the product configuration shipped to the browser lacks proxyEndpointTemplate. This is the typical 'open a forwarded port in browser' path.

Common situations: Running a fork/build that does not inject productConfiguration.proxyEndpointTemplate into the browser config; disabling the proxy feature while extensions still try to resolve localhost URIs; a version mismatch where the template key was renamed.

Related errors


AI-assisted analysis of coder/code-server@51f90a376b (2026-08-12). Data as JSON: /api/errors/36e47054251f73fd. Report an issue: GitHub.