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
- Read the appended stderr detail — it usually says the root cause (connection refused, timeout, permission denied)
- Test manually: ssh -M -N -f <host> with the same key/config
- Verify host/port reachability: ping/nc -vz <host> 22
- Confirm the key is authorized: ssh-copy-id <host> or check authorized_keys
- 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
- Test `ssh <host> true` manually before automating
- Use ssh-copy-id to install keys
- Connect VPN before reaching internal hosts
- Keep ~/.ssh/config Host entries consistent with tool targets
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
- AnthropicConnectionError
- ssh reverse forward to ${config.sshTarget} exited with code
- Gemini Files API upload initialization request failed
- Gemini Files API upload finalization request failed
- ssh exited with code ${exitCode}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/670960f595a957d4.
Report an issue: GitHub.