moeru-ai/airi · error · Error

Beat Sync is not available in Stage Pocket

Error message

Beat Sync is not available in Stage Pocket

What it means

On Stage Pocket (the Capacitor mobile build), beat-sync has no implementation, so the Eventa invoke handler for beatSyncToggleInvokeEventa is registered to throw this error whenever it is invoked with enabled=true (packages/stage-shared/src/beat-sync/detector.ts:256). It is a deliberate capability guard, not a bug: desktop capture analysis cannot run on the mobile platform.

Solutions

  1. Gate the toggle UI on platform (hide it on Stage Pocket) so the invoke never fires
  2. Catch this error where toggle is invoked and show an 'unsupported on this device' notice
  3. Use the state getters (beatSyncGetStateInvokeEventa returns isActive:false) to render the off state instead of toggling

Example fix

// before
await invoke(beatSyncToggleInvokeEventa, true) // throws on Pocket
// after
import { isCapacitorNative } from '../utils/platform'
if (!isCapacitorNative())
  await invoke(beatSyncToggleInvokeEventa, true)
Defensive patterns

Strategy: validation

Validate before calling

import { Capacitor } from '@capacitor/core'

const beatSyncSupported = !Capacitor.isNativePlatform() // Pocket = native Capacitor build
if (beatSyncSupported)
  await invoke(beatSyncToggleInvokeEventa, enabled)

Type guard

const isStagePocket = (): boolean => Capacitor.isNativePlatform()

function supportsBeatSync(env: 'web' | 'tamagotchi' | 'pocket'): boolean {
  return env !== 'pocket'
}

Try / catch

try {
  await invoke(beatSyncToggleInvokeEventa, true)
}
catch (error) {
  if (error instanceof Error && error.message === 'Beat Sync is not available in Stage Pocket') {
    toast('Beat Sync is not supported on this device')
    return
  }
  throw error
}

Prevention

When it happens

Trigger: UI code shared across platforms calling the beat-sync toggle with enabled=true inside the Pocket build, or a settings page that was not platform-gated and lets the user flip the toggle on mobile.

Common situations: Reusing the desktop settings UI on mobile, feature flags that forget to exclude pocket, or code paths migrated without a platform check.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/965ebfc1aac1e61c. Report an issue: GitHub.

Appendix: source

Thrown at packages/stage-shared/src/beat-sync/detector.ts:256

}

let detector: BeatSyncDetector | undefined
function getDetector() {
  if (!isStageWeb())
    throw new Error('getDetector() is only available in Stage Web environment')

  if (!detector)
    detector = createBeatSyncDetector({ env: StageEnvironment.Web })

  return detector
}

let context: ReturnType<typeof createContext> | undefined

function installCapacitorHandlers(context: ReturnType<typeof createContext>) {
  defineInvokeHandler(context, beatSyncToggleInvokeEventa, async (enabled) => {
    if (enabled)
      throw new Error('Beat Sync is not available in Stage Pocket')
  })
  defineInvokeHandler(context, beatSyncGetStateInvokeEventa, async () => ({ isActive: false }))
  defineInvokeHandler(context, beatSyncUpdateParametersInvokeEventa, async () => {})
  defineInvokeHandler(context, beatSyncGetInputByteFrequencyDataInvokeEventa, async () => {
    return new Uint8Array(inputAnalyserFFTSize / 2)
  })
}

function getContext() {
  if (!context) {
    context = createContext()

    // Capacitor cannot capture system audio. Register every request handler so
    // callers receive deterministic inactive results instead of pending forever.
    if (isStageCapacitor())
      installCapacitorHandlers(context)
  }

View on GitHub (pinned to 677329427f)