Error handling

Every error code, when it fires, and how to recover.

Every failure mode in the SDK surfaces as a GameeError — either thrown synchronously (validation, missing capability, calling init() twice) or as a Promise rejection (everything that touches the wire).

import { GameeError } from '@gamee/sdk';

try {
  await gamee.purchaseItemWithGems({ gemsCost: 50, itemName: 'extra_life' });
} catch (err) {
  if (err instanceof GameeError) {
    console.warn(err.code, err.method, err.message, err.cause);
  } else {
    throw err; // not ours — rethrow
  }
}

The GameeError shape#

PropertyTypeRequiredNotes
name'GameeError'trueStandard Error subclass.
codeGameeErrorCodetrueThe branchable identifier (see table below).
methodstringfalseWhich SDK method produced the error, if applicable.
messagestringtrueHuman-readable. Don’t parse — branch on code.
causeunknowntrueThe underlying error if there was one.

GameeErrorCode is exported as a TypeScript union for exhaustive switch:

import type { GameeErrorCode } from '@gamee/sdk';

function explain(code: GameeErrorCode): string {
  switch (code) {
    case 'VALIDATION':
      return 'Bad input.';
    case 'CAPABILITY_MISSING':
      return 'Declare the capability in init().';
    case 'BRIDGE_TIMEOUT':
      return 'Platform did not respond.';
    case 'BRIDGE_ERROR':
      return 'Platform returned an error.';
    case 'BRIDGE_OVERFLOW':
      return 'Too many in-flight requests.';
    case 'INVALID_STATE':
      return 'Wrong time to call this.';
    case 'NOT_SUPPORTED':
      return 'Method not available on this platform.';
  }
}

Codes#

VALIDATION#

Inputs failed the SDK’s local validation before anything reached the platform.

Triggered by:

  • Unknown capability in init({ capabilities }).
  • logEvent with empty/oversized name (max 24) or value (max 160).
  • updateMissionProgress outside [0, 100] or non-finite.
  • updateScore with non-finite score, negative playTime, or empty checksum.
  • purchaseItemWithGems missing itemName/gemsCost or with negative gemsCost.

Recovery: fix the call site; this is a programmer error.

CAPABILITY_MISSING#

You called a gated method without declaring the matching capability.

const sdk = createSdk();
await sdk.init({ capabilities: [] }); // no 'saveState'
sdk.gameSaveState({}); // throws CAPABILITY_MISSING

Recovery: add the capability to your init() call. See capabilities for which methods need which capability.

BRIDGE_TIMEOUT#

The platform did not respond within requestTimeoutMs (default 30 000 ms). Only data-returning methods can hit this — fire-and-forget signals don’t track responses.

Recovery: retry once on user action, then surface a soft error to the player. If you see this in production from a real platform, file a bug — the platform is supposed to respond.

async function safeShowAd(): Promise<boolean> {
  try {
    return (await gamee.showRewardedVideo()).videoPlayed;
  } catch (err) {
    if (err instanceof GameeError && err.code === 'BRIDGE_TIMEOUT') return false;
    throw err;
  }
}

BRIDGE_ERROR#

The platform returned an explicit error response. The platform’s error code/message are forwarded into the GameeError’s message (and cause when available).

Recovery: depends on the method. Treat purchase/ad failures as “the action didn’t happen” and re-prompt the user.

BRIDGE_OVERFLOW#

You have more than maxPendingRequests data-returning calls in flight (default 256). The oldest pending request is rejected with this code so the new one has a slot.

Recovery: don’t fan out hundreds of purchaseItemWithGems / loadRewardedVideo calls. If you legitimately need a higher cap, raise maxPendingRequests in SdkOptions.

INVALID_STATE#

Method called at the wrong time. The two cases that matter:

  • init() called twice. Use createSdk() for a fresh instance instead.
  • Anything called after dispose(). All in-flight Promises reject with this code.

NOT_SUPPORTED#

Reserved for future use — a method exists in the API but the platform you’re running on can’t honor it. The current SDK does not throw this code from any of its public methods, but check for it in long-lived switches so adding new methods later is a non-breaking change.

Top-level safety net#

For long-lived games it’s reasonable to set a top-level handler that logs unexpected errors to your analytics:

window.addEventListener('unhandledrejection', (e) => {
  if (e.reason instanceof GameeError) {
    track('gamee_error', { code: e.reason.code, method: e.reason.method });
  }
});

If you have logEvents declared, you can ship a summary to the platform too:

window.addEventListener('unhandledrejection', (e) => {
  if (e.reason instanceof GameeError) {
    gamee.logEvent('gamee_err', `${e.reason.code}:${e.reason.method ?? ''}`);
  }
});

Switch to a desktop

The GAMEE SDK emulator and docs are built for screens wider than 1280 px. Open this page on a laptop or desktop browser to get the full experience.

If you're already on a wide screen, drag your window wider and this banner will disappear.