moeru-ai/airi · error

Vite did not expose a reachable dev server URL.

Error message

Vite did not expose a reachable dev server URL.

What it means

Thrown by pickServerUrl() when the Vite dev server exposes neither resolvedUrls.network nor resolvedUrls.local. cap-vite uses this URL to tell `cap run` where to load the bundled app from, so an absent URL makes the run target undefined.

Source

Thrown at packages/cap-vite/src/native.ts:165

      return capArgs
    }

    const targets = await listTargets(platform)
    target = targets.find(device => device.id)?.id
  }

  if (!target) {
    throw new Error(`No ${platform} devices or simulators found. Connect a device, start a simulator or emulator, or pass --target explicitly.`)
  }

  return [platformArg, '--target', target, ...rest]
}

export function pickServerUrl(server: Pick<ViteDevServer, 'resolvedUrls'>): URL {
  const url = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0]

  if (!url) {
    throw new Error('Vite did not expose a reachable dev server URL.')
  }

  return new URL(url)
}

export function shouldRestartForNativeChange(file: string, platform: CapacitorPlatform, cwd: string): boolean {
  const absoluteFile = resolve(cwd, file)
  const platformRoot = resolve(cwd, platform)

  if (!absoluteFile.startsWith(`${platformRoot}${sep}`) && absoluteFile !== platformRoot) {
    return false
  }

  const fileName = basename(absoluteFile)

  if (ignoredNames.has(fileName)) {
    return false
  }

View on GitHub (pinned to 27111382b4)

Solutions

  1. Ensure pickServerUrl is called after Vite has started listening (e.g. await server.listen() or inside a then resolvedUrls is set).
  2. Check vite config: server.host and server.port must permit a bound socket; avoid server.middlewareMode: true unless you manage URLs yourself.
  3. If using HMR over a custom server, verify server.hmr.server resolves so Vite can compute local/network URLs.

Example fix

// before
const url = pickServerUrl(server) // called too early in configureServer
// after
server.httpServer?.once('listening', () => {
  const url = pickServerUrl(server)
  startCapProcess(cwd, capArgs, url)
})
Defensive patterns

Strategy: validation

Validate before calling

function hasResolvedUrl(server: { resolvedUrls?: { network?: string[]; local?: string[] } }): boolean {
  return !!(server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0])
}

if (!hasResolvedUrl(server)) throw new Error('Vite server has no resolved URL yet')

Try / catch

try {
  const url = pickServerUrl(server)
} catch (error) {
  if (error instanceof Error && error.message.includes('reachable dev server URL')) {
    // wait for listening and retry once
  }
}

Prevention

When it happens

Trigger: Calling pickServerUrl(server) before the Vite server has finished binding its URLs; a Vite server configuration with server.host/server.port set in a way that prevents resolvedUrls from being populated; accessing the server in a context where listen() was never called or was closed.

Common situations: Custom Vite plugin that calls pickServerUrl in configureServer before the next tick when resolvedUrls are assigned; middleware mode servers that never call listen(); server.hmr.server misconfigured to a broken WebSocket; race after server.close().

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/cebefaa8c59ccb30. Report an issue: GitHub.