{"record":{"id":"017a6c1955bcb951","repo":"stablyai/orca","slug":"runtime-environment-pairing-changed-refresh-and-t","errorCode":null,"errorMessage":"Runtime environment pairing changed; refresh and try again","messagePattern":"Runtime environment pairing changed; refresh and try again","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/runtime-environments.ts","lineNumber":111,"sourceCode":"      }\n    ): Promise<{ subscriptionId: string; requestId: string }> => {\n      const subscriptionId =\n        typeof args.subscriptionId === 'string' && args.subscriptionId.length > 0\n          ? args.subscriptionId\n          : randomUUID()\n      if (remoteRuntimeSubscriptions.has(subscriptionId)) {\n        throw new Error('Runtime environment subscription id already exists')\n      }\n      const environment = resolveEnvironment(getUserDataPath(), args.selector)\n      if (isRuntimeEnvironmentManuallyDisconnected(environment.id)) {\n        throw new Error('runtime_manually_disconnected')\n      }\n      const pairingRevision = environment.pairingRevision ?? environment.createdAt\n      if (\n        args.expectedEnvironmentPairingRevision !== undefined &&\n        pairingRevision !== args.expectedEnvironmentPairingRevision\n      ) {\n        throw new Error('Runtime environment pairing changed; refresh and try again')\n      }\n      const transportGeneration = getRuntimeEnvironmentTransportGeneration(environment.id)\n      const transportIsCurrent = (): boolean =>\n        getRuntimeEnvironmentTransportGeneration(environment.id) === transportGeneration\n      const sender = event.sender\n      const ownerWebContentsId = sender.id\n      let senderDestroyed = sender.isDestroyed()\n      let subscription: RemoteRuntimeSubscription | null = null\n      let destroyedListenerAttached = false\n      const removeDestroyedListener = (): void => {\n        if (!destroyedListenerAttached) {\n          return\n        }\n        destroyedListenerAttached = false\n        sender.removeListener('destroyed', closeSubscription)\n      }\n      const closeSubscription = (): void => {\n        senderDestroyed = true","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/runtime-environments.ts#L93-L129","documentation":"Thrown at the start of a remote runtime subscription when args.expectedEnvironmentPairingRevision is provided and does not match the environment's current pairingRevision (falling back to createdAt). This is an optimistic-concurrency guard: pairing revisions change whenever the environment is paired/re-paired to a host, so a mismatch means the client's view is stale.","triggerScenarios":"Opening a remote runtime stream with a cached pairingRevision while the environment was re-paired (e.g. reconnection, re-keying, or server-side rotation) in another window or session. The handler also pre-checks manual-disconnect and duplicate subscription id before reaching this guard.","commonSituations":"Multiple windows where one re-pairs the environment; a long-open client whose cached revision aged out; automated re-pair flows during a session; restored session state pointing at an old revision.","solutions":["Catch the error, re-resolve the environment to refresh pairingRevision, and retry the subscription once.","Do not cache pairingRevision beyond the current view; fetch it fresh before subscribing.","Subscribe to runtime environment change events to invalidate the cached revision proactively.","If retries keep failing, prompt the user to re-pair the environment."],"exampleFix":"// before\nipc.invoke('runtimeEnvironments:subscribe', { selector, expectedEnvironmentPairingRevision: cached })\n\n// after\ntry {\n  await ipc.invoke('runtimeEnvironments:subscribe', { selector, expectedEnvironmentPairingRevision: cached })\n} catch (e) {\n  if (!/pairing changed/.test(e.message)) throw e\n  const fresh = await ipc.invoke('runtimeEnvironments:resolve', { selector })\n  await ipc.invoke('runtimeEnvironments:subscribe', {\n    selector,\n    expectedEnvironmentPairingRevision: fresh.pairingRevision ?? fresh.createdAt,\n  })\n}","handlingStrategy":"retry","validationCode":"const env = await ipc.invoke('runtimeEnvironments:resolve', { selector })\nconst rev = env.pairingRevision ?? env.createdAt\n// pass rev as expectedEnvironmentPairingRevision; refresh env before each attempt\nawait ipc.invoke('runtimeEnvironments:subscribe', { selector, expectedEnvironmentPairingRevision: rev })","typeGuard":"function hasPairingRevision(env: { pairingRevision?: unknown; createdAt: unknown }): boolean {\n  return typeof env.pairingRevision !== 'undefined' || typeof env.createdAt !== 'undefined'\n}","tryCatchPattern":"async function subscribe(selector: string, rev: unknown) {\n  try {\n    return await ipc.invoke('runtimeEnvironments:subscribe', { selector, expectedEnvironmentPairingRevision: rev })\n  } catch (e) {\n    if (!/pairing changed/.test((e as Error).message)) throw e\n    const fresh = await ipc.invoke('runtimeEnvironments:resolve', { selector })\n    return ipc.invoke('runtimeEnvironments:subscribe', {\n      selector,\n      expectedEnvironmentPairingRevision: fresh.pairingRevision ?? fresh.createdAt,\n    })\n  }\n}","preventionTips":["Do not cache pairingRevision beyond the current view; fetch fresh before subscribing.","Invalidate the cached revision when runtime environment change events fire.","Retry at most once after a refresh to avoid loops."],"tags":["ipc","runtime-environment","optimistic-concurrency","pairing","subscription"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}