stablyai/orca · error

Couldn't verify the SSH connection. Reconnect the host and t

Error message

Couldn't verify the SSH connection. Reconnect the host and try again.

What it means

Thrown by buildMobileFileMutationOwnership when worktreeHostId is a defined non-undefined value but parseExecutionHostId could not parse it into a recognized host (local | runtime | ssh). This guards against committing a file mutation to a host whose execution identity the client cannot interpret — writing through an ambiguous host id would risk mutating the wrong machine.

Source

Thrown at mobile/src/files/mobile-file-mutation-ownership.ts:22

import type { SshConnectionState, SshMutationExpectation } from '../../../src/shared/ssh-types'
import type { RpcClient } from '../transport/rpc-client'
import type { RpcFailure, RpcSuccess } from '../transport/types'

const FILE_MUTATION_TIMEOUT_MS = 15_000
const SSH_OWNER_CHANGED_MESSAGE =
  "Couldn't verify the SSH connection. Reconnect the host and try again."

export type MobileFileMutationOwnership = SshMutationExpectation & {
  expectedExecutionHostId: 'local' | `ssh:${string}`
}

export function buildMobileFileMutationOwnership(
  worktreeHostId: string | null | undefined,
  sshState: SshConnectionState | null = null
): MobileFileMutationOwnership {
  const host = parseExecutionHostId(worktreeHostId)
  if (worktreeHostId !== undefined && !host) {
    throw new Error(SSH_OWNER_CHANGED_MESSAGE)
  }
  if (!host || host.kind === 'local' || host.kind === 'runtime') {
    return { expectedExecutionHostId: 'local' }
  }
  if (sshState?.targetId !== host.targetId || sshState.connectionGeneration === undefined) {
    throw new Error(SSH_OWNER_CHANGED_MESSAGE)
  }
  return {
    expectedExecutionHostId: host.id,
    expectedSshTargetId: host.targetId,
    expectedSshConnectionGeneration: sshState.connectionGeneration
  }
}

export async function captureMobileFileMutationOwnership(
  client: Pick<RpcClient, 'sendRequest'>,
  worktree: string
): Promise<MobileFileMutationOwnership> {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Update the mobile app so parseExecutionHostId understands the new host-kind prefix.
  2. If worktreeHostId came from worktree.show, re-fetch it — a corrupt value often indicates a stale worktree row; rotate the worktree.
  3. Treat as 'reconnect the host': the SSH_OWNER_CHANGED_MESSAGE tells the user to re-establish the host connection so a fresh, parseable hostId is captured.
  4. If the hostId is genuinely a new kind and updating is not possible, block mutations on that worktree until the app is upgraded.

Example fix

// before
const host = parseExecutionHostId(worktreeHostId)
if (worktreeHostId !== undefined && !host) {
  throw new Error(SSH_OWNER_CHANGED_MESSAGE)
}

// after — distinguish 'new kind' from 'corrupt' for clearer UX
const host = parseExecutionHostId(worktreeHostId)
if (worktreeHostId !== undefined && !host) {
  if (typeof worktreeHostId === 'string' && worktreeHostId.includes(':')) {
    throw new Error(`Unsupported host kind: ${worktreeHostId.split(':')[0]}. Update the app.`)
  }
  throw new Error(SSH_OWNER_CHANGED_MESSAGE)
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the host id is parseable before building ownership
import { parseExecutionHostId } from '../../../src/shared/execution-host'
if (worktreeHostId !== undefined && !parseExecutionHostId(worktreeHostId)) {
  throw new Error('Cannot mutate files: unrecognized host id. Reconnect the host.')
}

Type guard

function isRecognizedHostId(id: string | null | undefined): boolean {
  if (id === undefined || id === null) return true
  return parseExecutionHostId(id) !== null
}

Try / catch

try {
  return buildMobileFileMutationOwnership(worktreeHostId, sshState)
} catch (err) {
  if (err instanceof Error && err.message === SSH_OWNER_CHANGED_MESSAGE) {
    await forceReconnect()
    const fresh = await captureMobileFileMutationOwnership(client, worktree)
    return fresh
  }
  throw err
}

Prevention

When it happens

Trigger: buildMobileFileMutationOwnership(worktreeHostId, sshState) is called with worktreeHostId that is defined (not undefined) but not 'local', not a 'runtime:...' id, and not an 'ssh:...' id. Concretely: a malformed host string from worktree.show, a new host-kind prefix the client build does not yet understand, or a corrupted persisted host id.

Common situations: A newer desktop build introduces a new execution-host kind (e.g., 'container:...') that this mobile build's parseExecutionHostId does not recognize; a worktree.show result with a truncated or corrupt hostId; an old mobile app paired with a newer desktop that emits a different host id scheme.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/68186440bdf13072. Report an issue: GitHub.