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
- Ensure pickServerUrl is called after Vite has started listening (e.g. await server.listen() or inside a then resolvedUrls is set).
- Check vite config: server.host and server.port must permit a bound socket; avoid server.middlewareMode: true unless you manage URLs yourself.
- 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
- Call pickServerUrl only after server.listen() has resolved or the 'listening' event fired.
- Avoid server.middlewareMode unless you manage URLs yourself.
- Log server.resolvedUrls during dev to catch empty states early.
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
- AIRI host websocket bridge is unavailable
- Godot stage is not running.
- Godot stage bridge is not connected.
- Previous Godot stage process is still shutting down. Retry a
- Extension asset server base URL is unavailable; start the as
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/cebefaa8c59ccb30.
Report an issue: GitHub.