can1357/oh-my-pi · error

Failed to start SSH master for ${target}${detail}

Error message

Failed to start SSH master for ${target}${detail}

What it means

The manager opens a ControlMaster (`ssh -M -N -f`) to multiplex connections over one authenticated socket. If that master process exits non-zero, this error is thrown, appending the master's stderr for diagnosis. It wraps whatever underlying ssh failure occurred (auth, DNS, timeout, refused connection).

Source

Thrown at packages/coding-agent/src/ssh/connection-manager.ts:775

			if (!hostInfoCache.has(key) && !(await loadHostInfoFromDisk(host))) {
				await probeHostInfo(host);
			}
			return;
		}

		const check = await runSshSync(["-O", "check", ...buildCommonArgs(host), target]);
		if (check.exitCode === 0) {
			activeHosts.set(key, host);
			if (!hostInfoCache.has(key) && !(await loadHostInfoFromDisk(host))) {
				await probeHostInfo(host);
			}
			return;
		}

		const start = await runSshSync(["-M", "-N", "-f", ...buildCommonArgs(host), target]);
		if (start.exitCode !== 0) {
			const detail = start.stderr ? `: ${start.stderr}` : "";
			throw new Error(`Failed to start SSH master for ${target}${detail}`);
		}

		activeHosts.set(key, host);
		if (!hostInfoCache.has(key) && !(await loadHostInfoFromDisk(host))) {
			await probeHostInfo(host);
		}
	})();

	pendingConnections.set(key, promise);
	try {
		await promise;
	} finally {
		pendingConnections.delete(key);
	}
}

export async function invalidateHostMetadata(hostNames: Iterable<string>): Promise<void> {
	const names = [...hostNames];

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the appended stderr detail — it usually says the root cause (connection refused, timeout, permission denied)
  2. Test manually: ssh -M -N -f <host> with the same key/config
  3. Verify host/port reachability: ping/nc -vz <host> 22
  4. Confirm the key is authorized: ssh-copy-id <host> or check authorized_keys
  5. Check ~/.ssh/config and any per-target overrides for wrong User/Port/IdentityFile

Example fix

// before
{ name: "prod", host: "10.0.0.99", port: 22 } // host offline -> master fails
// after: fix host or connect VPN first
{ name: "prod", host: "10.0.0.5", port: 22 }
Defensive patterns

Strategy: retry

Validate before calling

import { $ } from "bun";
// preflight: plain ssh reachability before attempting master
const probe = await $`ssh -o BatchMode=yes -o ConnectTimeout=5 ${target} true`.quiet().nothrow();
if (probe.exitCode !== 0) console.error("ssh target unreachable/auth failing:", probe.stderr.toString());

Try / catch

try {
  await connect(target);
} catch (err) {
  if (err instanceof Error && err.message.startsWith("Failed to start SSH master for")) {
    const detail = err.message.split(": ").slice(1).join(": ");
    if (/timeout|Connection refused|unreachable/i.test(detail)) {
      await Bun.sleep(1000); // retry with backoff for transient network
      return connect(target);
    }
    if (/permission denied|publickey/i.test(detail)) console.error("Fix key auth (ssh-copy-id)");
  }
  throw err;
}

Prevention

When it happens

Trigger: runSshSync(["-M","-N","-f",...]) returning non-zero: unreachable host/port, authentication failure, ControlPersist/socket path issues, or the remote refusing sessions.

Common situations: Wrong host/port in config, key not authorized on remote (Permission denied (publickey)), VPN not connected, host DNS changed, too many concurrent masters on a flaky network.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/670960f595a957d4. Report an issue: GitHub.