{"record":{"id":"3c2c39a1379085f9","repo":"stablyai/orca","slug":"relay-credential-resolved-as-an-unexpected-credent-3c2c39","errorCode":null,"errorMessage":"relay credential resolved as an unexpected credential kind","messagePattern":"relay credential resolved as an unexpected credential kind","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mobile/src/transport/mobile-relay-physical-client.ts","lineNumber":135,"sourceCode":"  function acceptRelayHello(raw: unknown): void {\n    if (typeof raw !== 'string') {\n      throw new Error('expected plaintext relay hello')\n    }\n    let value: unknown\n    try {\n      value = JSON.parse(raw)\n    } catch {\n      throw new Error('invalid relay hello JSON')\n    }\n    const parsed = RelayPhoneHelloSchema.safeParse(value)\n    if (!parsed.success) {\n      throw new Error('invalid relay hello')\n    }\n    if (!parsed.data.ok) {\n      throw new RelayOuterError(parsed.data.code)\n    }\n    if (parsed.data.credentialKind !== (args.expectedCredentialKind ?? 'invite')) {\n      throw new Error('relay credential resolved as an unexpected credential kind')\n    }\n    outerReady = true\n    log('info', 'Relay: cell accepted credential', 'Starting E2EE handshake')\n    channel.start()\n  }\n\n  function fail(error: Error): void {\n    if (closed) {\n      return\n    }\n    closed = true\n    if (intentionallyClosed) {\n      log('info', 'Relay: pairing socket closed', cellHost)\n    } else {\n      log('warn', 'Relay: pairing socket closed', pairingRelayErrorDetail(error))\n    }\n    channel.dispose()\n    rejectAuthenticated(error)","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/transport/mobile-relay-physical-client.ts#L117-L153","documentation":"Thrown in connectMobileRelayForPairing's acceptRelayHello when the cell hello succeeded (ok:true) but parsed.data.credentialKind does not equal args.expectedCredentialKind ?? 'invite'. The cell resolved the credential as a different kind than the client expects (default invite).","triggerScenarios":"The caller passed an invite token but set expectedCredentialKind:'resume', or passed a resume token while leaving the default 'invite'; the cell promoted an invite to resume after install completed.","commonSituations":"State-machine bug mislabeling the credential kind; pairing already completed so the invite is interpreted as resume; the caller forgot to set expectedCredentialKind on a resume dial.","solutions":["Pass expectedCredentialKind explicitly on every connectMobileRelayForPairing call and ensure it matches the credential string.","If the host already installed the credential, switch to the resume flow with the resume token.","Regenerate a credential of the correct kind if the stored one was promoted."],"exampleFix":"// before\nconnectMobileRelayForPairing({\n  relay,\n  credential: resumeToken,\n  // expectedCredentialKind omitted -> defaults to 'invite', token is resume\n  ...\n})\n// after\nconnectMobileRelayForPairing({\n  relay,\n  credential: resumeToken,\n  expectedCredentialKind: 'resume',\n  ...\n})","handlingStrategy":"validation","validationCode":"function assertCredentialKindOption(credentialKind: 'invite' | 'resume' | undefined, credential: string): 'invite' | 'resume' {\n  const kind = credentialKind ?? 'invite'\n  if (!credential) throw new Error('missing credential for kind ' + kind)\n  return kind\n}","typeGuard":null,"tryCatchPattern":"try {\n  const client = connectMobileRelayForPairing(args)\n} catch (error) {\n  if (error instanceof Error && error.message === 'relay credential resolved as an unexpected credential kind') {\n    // host may have installed; switch to resume flow with the resume token\n  }\n}","preventionTips":["Always pass expectedCredentialKind explicitly on every connectMobileRelayForPairing call.","Track credential provenance alongside the token so the kind is unambiguous.","Flip to the resume flow as soon as the host reports install complete."],"tags":["relay","authentication","pairing","state-machine"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}