{"record":{"id":"ea1dd2ff23f0fa81","repo":"stablyai/orca","slug":"ssh-target-did-not-connect-state-status","errorCode":null,"errorMessage":"SSH target did not connect: ${state.status}","messagePattern":"SSH target did not connect: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/ephemeral-vm-runtime-ssh.ts","lineNumber":33,"sourceCode":"export type RuntimeOwnedSshConnectionResult = {\n  targetId: string\n  target: SshTarget\n}\n\nexport async function connectRuntimeOwnedSshTarget(args: {\n  runtimeId: string\n  connection: Extract<EphemeralVmRecipeConnection, { type: 'ssh' }>\n  signal?: AbortSignal\n}): Promise<RuntimeOwnedSshConnectionResult> {\n  const store = getSshConnectionStore()\n  if (!store) {\n    throw new Error('SSH handlers are not registered.')\n  }\n  const target = store.upsertRuntimeOwnedTarget(args.runtimeId, args.connection.target)\n  try {\n    const state = await connectRegisteredSshTarget(target.id)\n    if (state.status !== 'connected') {\n      throw new Error(state.error || `SSH target did not connect: ${state.status}`)\n    }\n    await waitForRuntimeSshProviders(target.id, args.signal)\n  } catch (error) {\n    // The target is persisted at upsert, so a failed connect/provider-wait would\n    // orphan it; remove it (idempotent) before rethrowing so cleanup is complete.\n    await removeRuntimeOwnedSshTarget(target.id).catch(() => undefined)\n    throw error\n  }\n  return { targetId: target.id, target }\n}\n\nexport async function disconnectRuntimeOwnedSshTarget(targetId: string | undefined): Promise<void> {\n  if (!targetId) {\n    return\n  }\n  await disconnectRegisteredSshTarget(targetId)\n}\n","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ephemeral-vm-runtime-ssh.ts#L15-L51","documentation":"connectRuntimeOwnedSshTarget called connectRegisteredSshTarget and the returned state.status is not 'connected', with no specific error message on the state — so the generic 'did not connect: <status>' is used. The target is removed (idempotently) before rethrow so a failed connect does not orphan a persisted target.","triggerScenarios":"ephemeral-vm-runtime-ssh.ts:31-33 — connectRegisteredSshTarget(target.id) returns a state whose status !== 'connected' AND state.error is empty, hitting the fallback template.","commonSituations":"SSH auth failure surfaced as a non-error status; host unreachable; the key is not loaded in the ssh agent; connection refused; a provider-registration race where the connection is left in a transitional state.","solutions":["Catch and log the full state object (status + error) instead of only the generic message.","Verify the SSH target host, port, and key in the connection config.","Ensure the SSH agent has the key (`ssh-add -l`).","Retry after confirming network reachability of the host."],"exampleFix":"// before\nconst { targetId } = await connectRuntimeOwnedSshTarget(args)\n\n// after\ntry {\n  const { targetId } = await connectRuntimeOwnedSshTarget(args)\n} catch (e) {\n  throw new Error(`SSH connect failed for ${args.connection.target.host}: ${e.message}`)\n}","handlingStrategy":"try-catch","validationCode":"const ok = await pingSshHost(args.connection.target.host, args.connection.target.port)\nif (!ok) { /* fix host/key/agent before connect */ }","typeGuard":"function isSshConnectError(e: unknown): boolean {\n  return e instanceof Error && /SSH target did not connect|SSH handlers are not registered|provider wait aborted/.test(e.message)\n}","tryCatchPattern":"try { return await connectRuntimeOwnedSshTarget(args) }\ncatch (e) {\n  if (isSshConnectError(e)) { throw new Error(`SSH connect failed for ${args.connection.target.host}: ${e.message}`) }\n  throw e\n}","preventionTips":["Confirm host/port reachability and key/agent before connecting.","Catch and log the full connection state, not just the generic message.","Retry once after verifying the SSH agent has the key (`ssh-add -l`)."],"tags":["ssh","ephemeral-vm","connection","network"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}