CherryHQ/cherry-studio · info · Error

Registration polling aborted

Error message

Registration polling aborted

What it means

Thrown by registrationPoll() when the passed AbortSignal is aborted before the deadline. The poll loop checks options.signal?.aborted at the top of each iteration and throws this constant message so callers can distinguish a user/system cancellation from a real failure.

Source

Thrown at src/main/ai/channels/adapters/feishu/FeishuAppRegistration.ts:96

    deviceCode,
    verificationUri,
    interval: (res.interval as number) ?? 5,
    expiresIn: (res.expires_in as number) ?? 600
  }
}

export async function registrationPoll(
  domain: FeishuDomain,
  deviceCode: string,
  options: { interval: number; expiresIn: number; signal?: AbortSignal }
): Promise<RegistrationResult> {
  const baseUrl = BASE_URLS[domain]
  const deadline = Date.now() + options.expiresIn * 1000
  let interval = options.interval * 1000

  while (Date.now() < deadline) {
    if (options.signal?.aborted) {
      throw new Error('Registration polling aborted')
    }

    await delay(interval, { signal: options.signal })

    const res = await postRegistration(baseUrl, {
      action: 'poll',
      device_code: deviceCode
    })

    // Success: got credentials
    if (res.client_id && res.client_secret) {
      const userInfo = res.user_info as Record<string, string> | undefined
      logger.info('Feishu app registration succeeded')
      return {
        appId: res.client_id as string,
        appSecret: res.client_secret as string,
        openId: userInfo?.open_id
      }

View on GitHub (pinned to 726446b54c)

Solutions

  1. Treat this error as benign cancellation, not a fault — catch it and surface 'cancelled' to the UI.
  2. Only abort the signal on explicit user cancellation or teardown; do not reuse general-purpose signals.
  3. Ensure abort() is called exactly once when the registration screen unmounts.
Defensive patterns

Strategy: try-catch

Validate before calling

// Only abort the signal on real cancellation/teardown.
const controller = new AbortController()
onUserCancel(() => controller.abort())
onTeardown(() => controller.abort())

Type guard

function isRegistrationAborted(e: unknown): e is Error {
  return e instanceof Error && e.message === 'Registration polling aborted'
}

Try / catch

try {
  await registrationPoll(domain, deviceCode, { interval, expiresIn, signal: controller.signal })
} catch (e) {
  if (isRegistrationAborted(e)) { logger.info('registration cancelled by user/teardown'); return }
  throw e
}

Prevention

When it happens

Trigger: Caller passes an AbortSignal that fires (abort()) while registrationPoll is looping between deadline checks; the next iteration observes signal.aborted and throws.

Common situations: User cancelled the QR-code registration flow from the UI, the app is shutting down and aborted in-flight polls, or a navigation/unmount aborted the controller.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/0e0b0f2a6a85b0db. Report an issue: GitHub.