heygen-com/hyperframes · error · AmbiguousPreviewServerError
Multiple Studio preview servers match this project (${ports.
Error message
Multiple Studio preview servers match this project (${ports.join(", ")}). Pass --port <port> to choose one. What it means
Thrown as AmbiguousPreviewServerError when more than one embedded Studio preview server matches the project directory and no --port was specified. The CLI cannot safely auto-select one, so it lists the matching ports and asks the user to disambiguate with --port.
Source
Thrown at packages/cli/src/utils/studioSelectionClient.ts:87
const normalizedProjectDir = normalizePath(projectDir);
const servers = await scan(startPort);
const embeddedServers = servers.filter(
(server) => normalizePath(server.projectDir) === normalizedProjectDir,
);
if (options.preferredPort !== undefined) {
const preferred = embeddedServers.find((server) => server.port === options.preferredPort);
if (preferred) return preferred;
const viteServer = await findViteStudioServerForProject(normalizedProjectDir, fetchImpl, [
options.preferredPort,
]);
if (viteServer) return viteServer;
if (embeddedServers.length > 0) {
throw new PreviewServerPortMismatchError(options.preferredPort, embeddedServers);
}
return null;
}
if (embeddedServers.length === 1) return embeddedServers[0]!;
if (embeddedServers.length > 1) throw new AmbiguousPreviewServerError(embeddedServers);
return findViteStudioServerForProject(normalizedProjectDir, fetchImpl);
}
export function studioSelectionUrl(server: ActiveServer): string {
return studioApiUrl(server, "selection");
}
export function studioApiUrl(server: ActiveServer, route: string): string {
const host = server.host ?? "127.0.0.1";
return `http://${host}:${server.port}/api/projects/${encodeURIComponent(server.projectName)}/${route}`;
}
// Vite dev servers bind IPv6 loopback (`::1`) by default while embedded servers
// bind IPv4 (`127.0.0.1`), so probe both — a single family misses the other and
// is exactly why `--selection`/`--context` failed against a local-studio preview.
const LOOPBACK_HOSTS = ["127.0.0.1", "[::1]"] as const;
async function findViteStudioServerForProject(View on GitHub (pinned to c2996c8626)
Solutions
- Pass --port with one of the ports listed in the error to choose which server to use.
- Kill the stale preview server process (check with tman list or ps) and retry without --port.
- If both servers are intentional, always specify --port to disambiguate.
Example fix
// before: hyperframes lint (multiple servers found) // after: hyperframes lint --port 3002
Defensive patterns
Strategy: validation
Validate before calling
import { scanActiveServers } from "../server/portUtils.js";
async function checkForDuplicateServers(projectDir: string): Promise<number[]> {
const servers = (await scanActiveServers()).filter(
(s) => normalizePath(s.projectDir) === normalizePath(projectDir),
);
return servers.map((s) => s.port);
} Type guard
function isAmbiguousPreviewServer(err: unknown): err is AmbiguousPreviewServerError {
return err instanceof AmbiguousPreviewServerError;
} Try / catch
try {
const server = await findPreviewServerForProject(dir);
} catch (err) {
if (err instanceof AmbiguousPreviewServerError) {
console.error(`Multiple servers on ports: ${err.ports.join(", ")}. Pass --port to choose.`);
process.exit(1);
}
throw err;
} Prevention
- Kill stale preview servers before starting new ones to avoid duplicates.
- Always specify --port when running multiple preview servers for the same project.
- Use tman list or ps to check for lingering server processes.
When it happens
Trigger: Two or more Studio preview server processes are running for the same project directory on different ports (e.g. the user started preview twice, or a previous instance didn't shut down). Without --port, the CLI finds multiple embeddedServers and throws.
Common situations: Starting a second preview without killing the first; a zombie Studio process from a crashed session; running preview in two terminal windows for the same project.
Related errors
- No Studio preview server for this project is running on port
- --remote-debugging-port must be an integer between 1 and 655
- selection endpoint returned ${response.status}
- --source must be 'sparticuz' or 'chrome-headless-shell' (got
- Unknown flag: ${arg}
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/28f2516508f7fdeb.
Report an issue: GitHub.