故障排查
修复常见安装、运行时、构建和发布问题。
大多数 Expo 模板问题来自四类原因:Metro 缓存、公开 env 被内联进 bundle、
用 Expo Go 测试原生模块功能,或者 variant 标识不匹配。
先运行模板预期的检查:
pnpm typecheck
pnpm lint
pnpm dev
pnpm dev 实际执行 expo start -c -p 3000,会清理 Metro cache 并使用文档
约定的开发端口。
更深一点的本地重置:
watchman watch-del-all
rm -rf .expo
pnpm dev
只有在你明确想删除 node_modules 时才用 pnpm clean。
| 症状 | 可能原因 | 修复 |
|---|
| Metro 启动了,但 app 仍显示旧文案或旧配置 | Env 已被内联,旧 bundle 还在运行 | 停掉 Metro,用 pnpm dev 重启,然后 reload app |
| 3000 端口被占用 | 另一个 dev server 正在运行 | 停掉另一个进程,或手动运行 expo start -c -p <port> |
| iOS 模拟器不出现 | 没安装 Xcode simulator runtime | 打开 Xcode 安装 simulator runtime,再运行 pnpm ios |
| Android 模拟器启动失败 | Android Studio SDK 或 JAVA_HOME 未配置 | 打开 Android Studio 安装 SDK/platform tools,并先在 Expo 外验证 emulator |
| 文件监听异常 | Watchman cache 过期 | 运行 watchman watch-del-all 并重启 Metro |
| 症状 | 可能原因 | 修复 |
|---|
| Supabase client 启动报错 | 缺少 EXPO_PUBLIC_SUPABASE_URL 或 EXPO_PUBLIC_SUPABASE_ANON_KEY | 复制 .env.example 到 .env.local,设置两个值并重启 Metro |
| 设置 key 后 provider 仍是 disabled | 旧 JS bundle 仍包含之前的 env | 清 cache 重启 Metro;EAS build 则需要重新构建 |
| secret 出现在 app bundle 中 | 服务端 secret 被放进 EXPO_PUBLIC_* | 轮换 secret,并移到 Supabase Edge secrets、EAS secrets 或 CI secrets |
| Preview build 连到 production backend | EAS profile 没设置对应 variant env | 检查 EAS env 和该 profile 的 APP_VARIANT |
EXPO_PUBLIC_* 值是公开的。不要把 service-role key、
REVENUECAT_WEBHOOK_AUTH、APNs keys 或 SENTRY_AUTH_TOKEN 放进去。
| 症状 | 可能原因 | 修复 |
|---|
| Purchases 显示未配置或不可用 | react-native-purchases 需要原生构建 | 使用 eas build --profile development --platform ios 或 Android 等价命令 |
| Google Sign-In 按钮失败 | 原生 Google Sign-In 模块和 OAuth clients 需要原生构建 | 配置 Google client IDs,并在 dev 或 store build 里测试 |
| 找不到 native module | Expo Go 没有打包该原生依赖 | 安装并打开模板的 development client |
| Push token 是 null | 模拟器、Web、权限拒绝或不是真机 token | 在真机测试并请求通知权限 |
| Haptics、biometrics 或 ATT 没反应 | 平台 fallback 或硬件不支持 | 在支持的 iOS/Android 真机上测试 |
Expo Go 仍然适合早期 UI 和 web-compatible 流程。Purchases、Google Sign-In、
真实通知、生物识别锁和 store-review 行为请用 development build。
| 症状 | 可能原因 | 修复 |
|---|
| 邮箱验证打开后卡住 | Supabase Auth 没有允许 redirect URL | 添加当前 variant 的 scheme callback |
| OAuth 回到错误的 app | bundle/scheme variant 不匹配 | 统一使用 soar-starter-expo-dev、soar-starter-expo-preview 或 soar-starter-expo |
| 原生 session 消失 | SecureStore 写入失败或 session 分片过期 | 重新登录、清 app 数据;如果自定义 session 变大,检查 lib/secure-store-chunked.ts |
| 未登录用户能看到公共页面 | 这是刻意设计 | 在操作处使用 SignInGate,或在新增路由中要求登录 |
| 症状 | 可能原因 | 修复 |
|---|
| Google 按钮提示未配置 | 缺少 EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID | 添加 Supabase Google provider 使用的 web client ID |
| iOS 原生 Google 流程失败 | 缺少或填错 EXPO_PUBLIC_GOOGLE_IOS_CLIENT_ID | 为当前测试 bundle ID 创建 iOS OAuth client |
| Android release signing 后登录失败 | Google Cloud 里的 SHA-1 fingerprint 不匹配 | 添加 debug、upload 或 Play signing certificate 的 fingerprint |
| Supabase 拒绝 token | Supabase 未配置 Google provider | 启用 Google provider,并使用相同 OAuth client 设置 |
| 症状 | 可能原因 | 修复 |
|---|
| Todos 或上传出现 RLS 错误 | migration 未应用 | 对 linked project 运行 supabase db push |
| TypeScript 不认识新表 | types/database.ts 过期 | 设置 SUPABASE_PROJECT_ID 并运行 pnpm gen:types |
| 上传示例提示 env 缺失 | Storage helper 读取公开 Supabase pair | 设置 EXPO_PUBLIC_SUPABASE_URL 和 EXPO_PUBLIC_SUPABASE_ANON_KEY |
| 头像上传失败但 sample upload 成功 | 头像使用和 sample uploads 不同的 bucket 路径 | 创建或检查 profile flow 需要的 avatar bucket 和策略 |
| 症状 | 可能原因 | 修复 |
|---|
| Paywall 没有 packages | 没有 current offering、产品未挂载,或商店元数据不完整 | 修复 RevenueCat offering,并等待商店同步 |
| RevenueCat 里购买成功但 app 仍是免费 | Supabase subscriptions 行缺失或不 entitled | 检查 webhook URL、REVENUECAT_WEBHOOK_AUTH 和 function logs |
| Development build 不能加载产品 | Bundle/package ID 与商店产品设置不匹配 | 为当前 variant 创建产品,或测试 production ID |
| Android purchase 不解锁 entitlement | 当前 webhook 忽略 PLAY_STORE | 先扩展 providerForStore() 和 billing provider enum |
| 症状 | 可能原因 | 修复 |
|---|
| 权限请求成功但没有服务端 push | 模板没有持久化 token | 添加 device_tokens 表并保存 Expo push token |
send-test-push 失败 | 可选函数查询了模板未提供的表 | 添加表和注册路径,或改用 Expo push API 发送 |
| Android 通知行为异常 | Notification channel 或权限状态不对 | 检查 lib/notifications.tsx 和 Android POST_NOTIFICATIONS 权限 |
| Web 没有真实 push 流程 | 当前实现偏原生 | 如果产品需要,添加独立 web push 实现 |
| 症状 | 可能原因 | 修复 |
|---|
| Custom scheme 链接打不开 | variant scheme 错误 | 使用 soar-starter-expo-dev://、soar-starter-expo-preview:// 或 soar-starter-expo:// |
| Universal/App Link 不打开 app | AASA 或 assetlinks 文件缺失/未验证 | 为 EXPO_PUBLIC_LINKING_DOMAIN 部署所需文件并重新构建原生 app |
| URL 打开 app 但没有进目标路由 | 路由不在 allow-list resolver 中 | 为你的产品路由更新 lib/deep-links.ts |
| 点击通知没有跳转 | 通知 payload 缺少支持的 URL | 发送 resolveDeepLinkHref() 接受的 URL |
| 症状 | 可能原因 | 修复 |
|---|
| OTA update 没到达 | Channel 或 runtime version 不匹配 | 发布到构建对应 channel 和 app version |
| OTA 后原生改动没生效 | OTA 不能发布原生模块、权限或 config plugin 变化 | 创建新的 EAS build |
| 商店包 app 名称或 bundle ID 错误 | APP_VARIANT 不是 production | 检查 production profile env 并重新构建 |
| Sentry sourcemap 缺失 | 上传时没有 SENTRY_AUTH_TOKEN | 把它存为 EAS 或 CI secret,并运行 sourcemap 上传步骤 |
| 版本门禁测试不出现 | 运行中的 bundle 没有新的 update config | 重启 Metro 或重新构建,或用 APP_UPDATE_CONFIG_URL 做远程覆盖 |