stablyai/orca · error
relay credential resolved as an unexpected credential kind
Error message
relay credential resolved as an unexpected credential kind
What it means
Thrown in MobileRelayE2eeLink.acceptHello when the hello succeeded (ok:true) but parsed.data.credentialKind ('invite'|'resume') does not equal this.options.expectedCredentialKind. The cell resolved the credential as a different kind than the client declared.
Source
Thrown at mobile/src/transport/mobile-relay-e2ee-link.ts:124
private acceptHello(raw: unknown): void {
if (typeof raw !== 'string') {
throw new Error('expected plaintext relay hello')
}
let value: unknown
try {
value = JSON.parse(raw)
} catch {
throw new Error('invalid relay hello JSON')
}
const parsed = RelayPhoneHelloSchema.safeParse(value)
if (!parsed.success) {
throw new Error('invalid relay hello')
}
if (!parsed.data.ok) {
throw new RelayOuterError(parsed.data.code)
}
if (parsed.data.credentialKind !== this.options.expectedCredentialKind) {
throw new Error('relay credential resolved as an unexpected credential kind')
}
this.outerReady = true
this.options.onHello?.(parsed.data)
this.channel.start()
}
private fail(error: Error): void {
if (this.closed) {
return
}
this.closed = true
this.channel.dispose()
this.options.onError(error)
this.socket.close()
}
}
function relaySocketUrl(endpoint: { cellUrl: string; relayHostId: string }): string {View on GitHub (pinned to 1136503c6a)
Solutions
- Verify the credential string and expectedCredentialKind agree at the MobileRelayE2eeLink construction site.
- If the host already installed the credential, switch to the resume flow (expectedCredentialKind:'resume' with the resume token).
- Regenerate a credential of the correct kind from the host if the stored one was promoted.
Example fix
// before
new MobileRelayE2eeLink({
credential: resumeToken,
expectedCredentialKind: 'invite', // wrong: token is a resume token
...
})
// after
new MobileRelayE2eeLink({
credential: resumeToken,
expectedCredentialKind: 'resume',
...
}) Defensive patterns
Strategy: validation
Validate before calling
function assertCredentialKind(credential: string, expectedCredentialKind: 'invite' | 'resume'): void {
// invite tokens and resume tokens are both 43-char base64url; the caller must
// track which one it holds. Assert the option matches the tracked provenance.
if (!credential) throw new Error('missing credential')
// expectedCredentialKind is the caller's source of truth, not derivable from the token
} Try / catch
try {
const link = new MobileRelayE2eeLink(options)
} catch (error) {
if (error instanceof Error && error.message === 'relay credential resolved as an unexpected credential kind') {
// re-evaluate whether the host already installed; switch to resume flow
}
} Prevention
- Track credential provenance (invite vs resume) alongside the token string so expectedCredentialKind is always correct.
- When the host reports install complete, flip the flow to resume and stop reusing the invite.
- Set expectedCredentialKind explicitly at every construction site.
When it happens
Trigger: The caller passed an invite token but set expectedCredentialKind:'resume', or passed a resume token while the option says 'invite'; the cell internally promoted an invite to resume after install completed.
Common situations: State-machine bug at the call site mislabeling the credential kind; pairing already completed on the host so the invite is no longer interpreted as invite; credential rotated between save and connect.
Related errors
- E2EE device authentication rejected
- relay_outer_${code}
- relay credential resolved as an unexpected credential kind
- E2EE v2 ready has not been accepted
- Invalid E2EE v2 authenticated response
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/544f2727b630c1d3.
Report an issue: GitHub.