Error codes
Remote calls fail with an InstfunError; branch on error.code and never parse the message. Local mode typically only produces INVALID_*, INVALID_SCORE and INSUFFICIENT_FUNDS.
InstfunError shape
class InstfunError extends Error {
readonly code: InstfunErrorCode;
readonly details?: Record<string, unknown>; // shape varies by code and is not guaranteed serializable
}The enum has 17 members: INVALID_CONFIG, INVALID_AMOUNT, INVALID_SCORE, INVALID_INPUT, INSUFFICIENT_FUNDS, REWARD_NOT_ALLOWED, RATE_LIMITED, UNAUTHORIZED, INVALID_CODE, EMAIL_ALREADY_BOUND, EMAIL_SEND_UNAVAILABLE, CONFLICT, NOT_FOUND, NETWORK, SERVER_ERROR, PAYLOAD_TOO_LARGE, UNKNOWN.
Error code table
| code | Trigger | Suggested handling |
|---|---|---|
INSUFFICIENT_FUNDS | wallet.spend() balance too low (details carry currency / balance / requested) | Do not retry; refresh the balance and tell the player |
INVALID_SCORE / INVALID_AMOUNT / INVALID_INPUT | Invalid arguments (score, amount, payload) | Fix the call; this is a code issue |
INVALID_CONFIG | Invalid initialization config (for example a bad gameId) | Fix the Instfun.init config |
NETWORK | Offline or request failure | Do not block gameplay; saves/records/leaderboard are already in the outbox, call sdk.sync() once connectivity returns; ask players to retry wallet and ad rewards later |
UNAUTHORIZED | Expired token (401) | Call Instfun.init again or reload; the SDK re-logs into the same account with the local deviceSeed |
INVALID_CODE | Wrong or expired email verification code (400) | Handled inside the email binding dialog: re-enter or resend |
EMAIL_ALREADY_BOUND | Email already bound to another account (409) | Use another email; the dialog shows an inline message |
EMAIL_SEND_UNAVAILABLE | Email service unavailable (503) | Try again later; the dialog shows an inline message |
RATE_LIMITED | Daily quota or frequency limit hit (429) | Saves go to the outbox and retry on the next sdk.sync(); do not auto-retry wallet or ads, show a "daily limit reached" message |
PAYLOAD_TOO_LARGE | Body or local validation over the limit (413) | Do not retry; trim or compress the data and resubmit |
CONFLICT | Ad receipt invalid, expired or already redeemed (409) | Treat as unrecoverable; ask the player to watch again, never auto-grant |
REWARD_NOT_ALLOWED | Credit exceeds the server reward policy (403) | Do not retry; check reason / amount against the policy |
NOT_FOUND | Resource missing (404) | Check identifiers such as boardId; usually a call-site error |
SERVER_ERROR | Server failure (5xx or a missing envelope) | Retry later; save operations enter the outbox automatically |
UNKNOWN | Unclassified failure | Log it and tell the player to try again later |
Example
The CDN bundle only exposes the global Instfun, so check error.code directly:
try {
await sdk.wallet.spend({ amount: 100, reason: "revive" });
} catch (error) {
switch (error?.code) {
case "INSUFFICIENT_FUNDS":
// Tell the player; do not retry
break;
case "NETWORK":
// Ask them to try later; saves/leaderboard reconcile on sdk.sync()
break;
case "RATE_LIMITED":
// Show "daily limit reached"; do not auto-retry
break;
default:
// Fallback: log error.code and details
break;
}
}With a source dependency you can import InstfunError from the SDK package and narrow with error instanceof InstfunError. Whichever integration you use, branch on error.code.
Automatic re-login on 401
- In remote mode the SDK exchanges a local deviceSeed for a server guest account. When the token expires (
UNAUTHORIZED), the SDK clears it, logs in again with the same deviceSeed (the same account) and retries the original call, such as the email code send/verify endpoints. - If the retry still fails, the original
UNAUTHORIZEDis thrown; the email binding dialog shows an inline "session expired, refresh the page" message. - When game code sees an unhandled
UNAUTHORIZED, callInstfun.initagain or reload the page. No local data needs to be cleared.
429, rate limits and offline advice
- There are two kinds of 429. Security-layer rate limits and daily quotas (saves/records/leaderboard) return
Retry-After, anddetailscontainsretryAfterSeconds/limit/window. Wallet daily reward caps and ad placement daily caps are legacy 429s carrying onlydailyCap/maxPerDay. - Saves, records and leaderboard submissions are retryable in the outbox and deferred to the next
sdk.sync(). Wallet and ad rewards surface the error to the game; do not retry automatically. - On
NETWORK, do not block gameplay: local-first data (saves/records/leaderboard) is already in the outbox; callsdk.sync()when connectivity returns or the page comes back to the foreground. - Responses without a JSON envelope also fall back by HTTP status: 401 →
UNAUTHORIZED, 404 →NOT_FOUND, 409 →CONFLICT, 413 →PAYLOAD_TOO_LARGE, 429 →RATE_LIMITED, and all other 5xx →SERVER_ERROR.
The submit page (
/platform/submit/) uses the website's own upload error codes (EMAIL_REQUIRED,INVALID_PACKAGE,GAME_ID_CONFLICT,PAYLOAD_TOO_LARGE,RATE_LIMITED,STORAGE_UNAVAILABLE), which do not pass through the SDK. See Publishing.
远端调用失败统一抛出 InstfunError,用 error.code 分支处理即可,不要解析 message。本地模式通常只会遇到 INVALID_*、INVALID_SCORE 与 INSUFFICIENT_FUNDS。
InstfunError 结构
class InstfunError extends Error {
readonly code: InstfunErrorCode;
readonly details?: Record<string, unknown>; // 结构随错误码而变,不保证可序列化
}错误码枚举共 17 个:INVALID_CONFIG、INVALID_AMOUNT、INVALID_SCORE、INVALID_INPUT、INSUFFICIENT_FUNDS、REWARD_NOT_ALLOWED、RATE_LIMITED、UNAUTHORIZED、INVALID_CODE、EMAIL_ALREADY_BOUND、EMAIL_SEND_UNAVAILABLE、CONFLICT、NOT_FOUND、NETWORK、SERVER_ERROR、PAYLOAD_TOO_LARGE、UNKNOWN。
错误码表
| code | 触发场景 | 处理建议 |
|---|---|---|
INSUFFICIENT_FUNDS | wallet.spend() 余额不足(details 含 currency / balance / requested) | 不要重试;刷新余额后提示玩家 |
INVALID_SCORE / INVALID_AMOUNT / INVALID_INPUT | 入参非法(分数、金额、载荷) | 修正调用参数;属于代码问题 |
INVALID_CONFIG | 初始化配置非法(如 gameId 格式错误) | 修正 Instfun.init 配置 |
NETWORK | 断网或请求失败 | 不要阻塞玩法;存档/记录/榜单已进 outbox,恢复后调用 sdk.sync();钱包与广告奖励提示稍后再试 |
UNAUTHORIZED | token 失效(401) | 重新 Instfun.init 或刷新页面,SDK 会用本机 deviceSeed 重新游客登录同一账号 |
INVALID_CODE | 邮箱验证码错误/过期(400) | 邮箱绑定弹窗内部处理:留在弹窗内重新输入或重发 |
EMAIL_ALREADY_BOUND | 邮箱已被其它账号绑定(409) | 换一个邮箱;绑定弹窗会内联提示 |
EMAIL_SEND_UNAVAILABLE | 邮件服务不可用(503) | 稍后再试;绑定弹窗会内联提示 |
RATE_LIMITED | 触发每日额度/频率限制(429) | 存档类自动进 outbox,下次 sdk.sync() 重试;钱包与广告不要自动重试,提示「今日已达上限」 |
PAYLOAD_TOO_LARGE | 请求体或本地校验超限(413) | 不要重试;压缩或裁剪数据后重新提交 |
CONFLICT | 广告票据无效、过期或已核销(409) | 视为不可恢复;提示重新观看,不要自动补发 |
REWARD_NOT_ALLOWED | 入账超出服务端奖励策略(403) | 不要重试;核对 reason / amount 是否符合策略 |
NOT_FOUND | 资源不存在(404) | 检查 boardId 等标识;通常为调用错误 |
SERVER_ERROR | 服务端异常(5xx 或无信封响应) | 可稍后重试;存档类操作会自动进入 outbox |
UNKNOWN | 未归类的异常 | 记录日志并提示玩家稍后再试 |
处理示例
CDN 单文件只暴露全局 Instfun,请直接检查 error.code:
try {
await sdk.wallet.spend({ amount: 100, reason: "revive" });
} catch (error) {
switch (error?.code) {
case "INSUFFICIENT_FUNDS":
// 提示余额不足,不要重试
break;
case "NETWORK":
// 提示稍后再试;存档/榜单类操作会在 sdk.sync() 时补偿
break;
case "RATE_LIMITED":
// 提示今日已达上限,不要自动重试
break;
default:
// 兜底:记录 error.code 与 details
break;
}
}使用源码依赖接入时,可以从 SDK 包导入 InstfunError 并用 error instanceof InstfunError 做类型收窄;无论哪种接入方式,分支判断都应以 error.code 为准。
401 自动重登
- 远端模式下,SDK 用本机 deviceSeed 换取服务端游客账号。token 失效(
UNAUTHORIZED)时,SDK 会清掉旧 token 并用同一 deviceSeed 重新登录同一账号,然后重试原调用(如邮箱验证码发送/校验接口)。 - 若重登后仍失败,接口原样抛出
UNAUTHORIZED;邮箱绑定弹窗会内联提示「登录状态失效,请刷新页面后重试」。 - 游戏侧遇到未处理的
UNAUTHORIZED时,重新Instfun.init或刷新页面即可,不需要清理本地数据。
429、限流与离线建议
- 429 分为两类:安全层限流与新增日配额(存档/记录/榜单)触发时返回
Retry-After,details含retryAfterSeconds/limit/window;钱包每日奖励上限与广告 placement 日限是既有 429,只含dailyCap/maxPerDay。 - 存档、记录与榜单在 outbox 中视为可重试,延迟到下次
sdk.sync();钱包与广告奖励原样抛给游戏,不要自动重试。 NETWORK时不要阻塞玩法:本地优先的数据(存档/记录/榜单)已进入 outbox,网络恢复或页面回到前台时调用sdk.sync()即可补偿。- 服务端对无 JSON 信封的响应也有状态码兜底映射:401 →
UNAUTHORIZED、404 →NOT_FOUND、409 →CONFLICT、413 →PAYLOAD_TOO_LARGE、429 →RATE_LIMITED,其余 5xx 归SERVER_ERROR。
上架页(
/platform/submit/)的上传接口使用网站自身的错误码(EMAIL_REQUIRED、INVALID_PACKAGE、GAME_ID_CONFLICT、PAYLOAD_TOO_LARGE、RATE_LIMITED、STORAGE_UNAVAILABLE),不经过 SDK。见「上架流程」。