Troubleshooting
errors and their fixes.
Where to find the logs, the exact messages the code prints, what each one means and how to test the outside services yourself. Tip: paste the exact error into the search box.
The commands on this page are written for macOS and Linux shells. On Windows, run them in Git Bash or WSL, where they work as written. In PowerShell, set a variable with $env:NAME="value" instead of export. Flutter, Node.js and the Firebase and FlutterFire CLIs run natively on Windows; iOS and macOS builds need a Mac.
01Where to look first
| Part | Where its messages appear |
|---|---|
| Wallet app | The terminal running flutter run. Lines start with INFO:, WARN: or ERROR:. The app logs only in debug builds (lib/utils/logger.dart), so reproduce problems with flutter run, not a release build. |
| Admin panel | The red message on the sign-in screen, and the browser's developer console (right-click → Inspect → Console). |
| Web wallet | The browser console. Content-Security-Policy and network errors show there. |
| Cloud Functions | The function logs (Firebase: viewing logs), or the command below. |
cd crypto_wallet
firebase functions:log -n 100 # last 100 lines, all functions
firebase functions:log --only publishAdminRemoteConfig -n 50
firebase functions:log --only validatePremiumEntitlement,recordFeeEvent
Always check which Firebase project the CLI talks to: firebase use. A different project than the one in firebase_options.dart explains many "it deployed but nothing changed" problems.
02Firebase credentials and flutterfire
WARN: FirebaseService: dev placeholder credentials detected
The wallet still has the placeholder lib/firebase_options.dart from the package. It starts, but sign-in, settings, push and PRO checks are off. Connect your project: flutterfire configure --platforms=ios,android,web in crypto_wallet (how).
The getter 'isPlaceholder' isn't defined for the type 'DefaultFirebaseOptions'
Or, from flutter run: Error: Member not found: 'isPlaceholder'. The wallet's start-up code (lib/core/firebase/firebase_service.dart) reads DefaultFirebaseOptions.isPlaceholder, and the file that flutterfire configure writes does not have it. Open crypto_wallet/lib/firebase_options.dart and add one line inside class DefaultFirebaseOptions {:
static const bool isPlaceholder = false;
Add it again each time you re-run flutterfire configure. The admin panel does not need it.
WARN: FirebaseService: initialization failed – running without Firebase
The file exists but Firebase refused it. Usually the platform you run on was not selected in flutterfire configure, or google-services.json / GoogleService-Info.plist belong to another project or bundle ID. Run flutterfire configure again with all platforms after any bundle-ID change.
Admin: "Firebase credentials are missing for this build"
The full message is "Firebase credentials are missing for this build. Configure real web Firebase options or enable ADMIN_DEMO_MODE explicitly." The admin still has the placeholder file (its API key starts with REPLACE_). Run flutterfire configure --platforms=web in crypto_wallet_admin and rebuild. Do not enable the demo mode for a real admin.
Admin: "Failed to initialize Firebase for the admin panel"
The options are real but initialisation failed. Check that the site's domain is in Firebase Authentication's authorized domains (Firebase Auth setup), and that the admin was built after flutterfire configure.
flutterfire: command not found
Run dart pub global activate flutterfire_cli, then add Dart's global bin folder to your PATH (the command prints the folder).
About setup_firebase.sh
crypto_wallet/setup_firebase.sh runs several of these steps for you. Start it with your project ID:
FIREBASE_PROJECT_ID=your-project ./setup_firebase.sh (required); it uses flutter and dart from your PATH unless you set FLUTTER=… / DART=….
After it runs, still add the isPlaceholder line above, deploy Remote Config with firebase deploy --only remoteconfig if its own step reports a failure, and create the first admin with Installation → First admin rather than the "next steps" it prints.
The step-by-step Installation is the reference.
03Cloud Functions deploy
Deploy fails with a Node version error
The functions need Node 22 (functions/package.json). nvm use 22, then deploy again.
Deploy fails because of billing, or asks to enable APIs
Switch the project to Blaze and allow the APIs the CLI asks for. The first deploy can take several minutes.
Deploy asks for OWNER_UID, CLAUDE_API_KEY or ADMIN_SETUP_TOKEN
All three are Secret Manager secrets. Set them with firebase functions:secrets:set OWNER_UID (a placeholder like unset is fine for an unused feature) and remove any OWNER_UID= line from functions/.env. Check what exists with firebase functions:secrets:get OWNER_UID.
TypeScript errors during deploy
The deploy runs npm run build first. Run it yourself in crypto_wallet/functions after npm ci to see the full error.
The app or admin gets "not-found" or CORS errors calling a function
All functions run in europe-west1. firebase functions:list must show 30 functions in that region. If you changed the region, change it everywhere (search the code for europe-west1).
bootstrapFirstAdmin answers "Bootstrap is fire-once and an admin already exists."
An entry in admin_users already exists, or the one-time lock _system/admin_bootstrap was written by an earlier successful call. Sign in with the existing admin. If you are still setting up and want to start over, delete the test entry in admin_users and the document _system/admin_bootstrap in the Firestore console, then call the function again. (A call that fails while creating the admin removes the lock by itself.)
04Firestore permission-denied
Firestore refuses a read or write that crypto_wallet/firestore.rules does not allow. Check in this order:
- Rules deployed to the right project
cd crypto_wallet firebase use firebase deploy --only firestore:rules - Signed in as an adminAdmin writes need an entry in
admin_userswith roleeditororsuperAdmin;vieweris read-only. - Super-admin-only keysPublishing the fee, fee wallets or maintenance mode as an editor fails with "Only super admins can modify '<key>'."
- Test a single requestSimulate the read or write with the user's UID in the Rules Playground (Firebase: Rules Playground).
05Admin sign-in and roles
At sign-in the panel loads admin_users/<your email> and refreshes your token's claims. You get in when that entry exists and is not disabled. Server functions additionally check the token claim admin: true, which bootstrapFirstAdmin, User Roles invitations and setAdminClaim set.
"This account is not authorized for the admin panel."
No admin_users entry for that email (entries are lower-case), the entry has disabled: true or active: false, or the rules were not deployed so the entry could not be read. Create the first admin (how) or ask a super admin to invite you.
"Admin only." when saving or publishing
Your entry exists but your token has no admin claim. A super admin re-invites you in User Roles (or the owner calls setAdminClaim); then sign out and in again so the new claim is loaded.
"This account is not provisioned for the admin control center."
A function found no admin profile for your login. Same fix as above.
"Invalid credentials. Check your email and password."
Wrong password, or the user does not exist in Firebase Authentication. Use Forgot password?.
There is no "Open Client Demo Workspace" button or demo login
Expected. Your build has no demo mode and no default credentials.
Locked out by the IP allowlist
Edit the document admin_config/ip_allowlist in Firestore (Firebase: manage data in the console), set enabled to false, reload.
Lost the authenticator phone
Use a backup code, or ask another super admin to disable your authenticator in User Roles.
06Remote Config and settings not reaching the app
- You pressed Publish to mobile app after saving.
- You fully closed and reopened the app (release builds fetch at most once an hour; debug builds every start).
- The app and the admin use the same Firebase project.
- If you set
ADMIN_CONFIG_URL: it points at your admin, returns JSON, and the app was rebuilt after you set it.
Check what is actually published
cd crypto_wallet
firebase remoteconfig:get -o current-remoteconfig.json # the admin_config value is inside
curl -s https://<your-admin-site>/config.json | head -c 400
{"error":"runtime_admin_config not found"} from /config.json means nothing was saved yet: save any admin page once.
Messages in the wallet's debug log
| Line | Meaning |
|---|---|
RemoteConfigService: admin_config key is empty, using defaults | No admin_config in Remote Config. Deploy the template once (how) or publish from the admin. |
RemoteConfigService: fetch failed – … | No network, or Firebase is not configured for this platform. |
[HttpAdminConfig] HTTP 404 from … | ADMIN_CONFIG_URL points at a wrong address, or the hosting rewrite to getRuntimeConfigJson is missing. |
[HttpAdminConfig] fetch failed – … | Network or CORS error. On the web, the config host must allow your wallet's origin; getRuntimeConfigJson sends Access-Control-Allow-Origin: *. |
[AdminConfigProvider] config applied (swap=…, feeBps=…) | The settings arrived. The values in brackets are what the app uses. |
Publishing fails
"Remote Config key is not allowed: <key>" means the admin sent a key the function does not accept; update both apps to the same version. "A valid fee wallet address is required when the fee rate is above 0" is explained under Swaps and fees.
07RPC connectivity
Each EVM chain has a main RPC address and fallbacks: built in (lib/core/chains.dart) or set in Admin → Chains.
At start the app sends eth_chainId to them in order and uses the first that answers with the right chain ID: within 2 seconds counts as healthy, 2 to 5 seconds as slow, otherwise unreachable. The choice is remembered on the device.
Solana, Bitcoin, Tron and Sui use the defaults in lib/core/chain_registry.dart (for Solana https://api.mainnet-beta.solana.com with public fallbacks).
A debug log line WARN: RPC health: <chain> (<id>) unreachable means none of the addresses answered. Test an address yourself:
# EVM: should return the chain ID (0x1 = Ethereum) and a block number
curl -s -X POST <rpc-url> -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'
curl -s -X POST <rpc-url> -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
# Solana: should return "ok"
curl -s <solana-rpc-url> -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"getHealth"}'
No answer, an HTML page or a wrong chain ID: replace the URL in Admin → Chains (it is tested when you save), then publish. Public RPCs rate-limit busy apps; for production, put an RPC address from your own provider account into Admin → Chains.
08Swaps and fees
Messages on the swap screen
| Message | Cause | Fix |
|---|---|---|
| "Swap provider rejected the request. Check your API keys in Settings." | HTTP 401 or 403 from 0x or 1inch: key missing, wrong or not allowed | Test the key with the curl commands on Services; fix it in .env or Admin → System Config → Remote API Keys |
| "Swap pricing is temporarily rate-limited. Please try again in a minute." | HTTP 429: your plan's request limit | Wait, or raise the plan with the provider |
| "Swap provider is temporarily unavailable. Please try again shortly." | HTTP 5xx at the provider | Wait; the other aggregator may answer meanwhile |
| "~ Indicative via CoinGecko" | No executable quote; only a market estimate | Add a 0x or 1inch key ("Add a DEX API key in Settings for executable on-chain swaps.") |
Swaps work but no fee arrives
- Fee wallet empty or fee set to Free (both are the defaults).
- You tested with a PRO account while the PRO waiver is on.
- The swap took a route that carries no fee: direct DEX routers, Tron or Sui (routes).
- You did not publish after Apply Fee Config.
- The debug log shows
[AdminConfigProvider] FEE MISCONFIGURATION: fee_bps=… but no valid fee wallet is set: EVM swaps are blocked until you set a valid wallet.
Jupiter: "no usable operator fee token account"
Debug log: [Jupiter] no usable operator fee token account for output mint <mint>; this swap runs without a platform fee. Your Solana fee wallet has no associated token account for the token the user received. Create it and check it with the getTokenAccountsByOwner command on Services. The app re-checks after about a minute.
"A valid fee wallet address is required when the fee rate is above 0"
Enter an EVM address: 0x followed by 40 hex characters, not all zeros. The Solana field needs a Solana address (base58, no 0x).
The fee I entered is refused
The maximum is 2 % (200 bps).
Fee events missing in Revenue
Run firebase functions:log --only recordFeeEvent. Fee event recorded means it worked; totals update after the nightly roll-up (02:00 UTC). Since 1.1.0 an event needs fee_bps from 1 to 200 and a transaction hash; older app versions are refused.
09WalletConnect
"WalletConnect not configured"
The wallet has no Reown project ID. Set REOWN_PROJECT_ID in .env or in Admin → System Config → Remote API Keys, publish, restart. To see missing keys at start, run a debug build with --dart-define=SHOW_STARTUP_KEY_SNACKBAR=true; it shows "Missing: WalletConnect Project ID".
Web: Connecting to 'wss://relay.walletconnect.org/…' violates the following Content Security Policy directive
Your host's Content-Security-Policy blocks WebSockets. The included firebase.json has connect-src 'self' https: wss:. On another host, add wss: to connect-src.
Pairing works but the dApp shows the wrong name
The name and website sent to dApps are set in code (No-code changes).
Also check that walletconnect_enabled is on in Admin → Remote Config.
10PRO purchases (App Store, Google Play)
The app sends each purchase to validatePremiumEntitlement. Read its log: firebase functions:log --only validatePremiumEntitlement.
| Message | Fix |
|---|---|
| "App Store entitlement verification is not configured." | Fill APP_STORE_KEY_ID, APP_STORE_ISSUER_ID, APP_STORE_PRIVATE_KEY in functions/.env, deploy. |
| "Google Play entitlement verification is not configured." | Fill GOOGLE_PLAY_SERVICE_ACCOUNT_JSON, deploy. |
| "Google Play verification credentials are malformed." | The JSON is not valid on one line. |
| "This product ID is not recognised as a valid PRO subscription product." | The store product ID differs from PRO_WEEKLY_PRODUCT_ID (and IAP_PRO_WEEKLY_ID in the app). |
| "The App Store subscription is no longer active." | Expired or refunded; expected. |
Also check that ANDROID_PACKAGE_NAME is your app ID. Setup steps: Apple, Google.
11Build and run
MissingPluginException or strange errors after changing packages
flutter clean
flutter pub getOn iOS also run pod install --repo-update in ios/.
"The current Dart SDK version is 3.11.0" on pub get
Both apps need Flutter 3.41.3 or newer (Dart 3.11.1): flutter upgrade.
iOS: "No profiles for …" or signing errors
In Xcode select your own Team under Signing & Capabilities and make sure the bundle ID is registered in your Apple Developer account.
The Android build runs out of memory, or is slow
android/gradle.properties gives Gradle 4 GB (-Xmx4G) and the Kotlin daemon 2 GB, which suits an 8 GB machine. With more RAM you can raise -Xmx (for example to -Xmx8G); with less, close other apps or lower it to -Xmx3G.
Google Play rejects the bundle as debug-signed
Signing values are missing. Create android/key.properties (how).
Push notifications do not arrive
iOS: upload the APNs key and keep the Push capability. All: the user must allow notifications and push_enabled must be on. Web: fill firebase-messaging-sw.js and build with WEB_PUSH_VAPID_KEY. Sends are logged as Push sent to topic by sendPushToTopic.
"AI features are not configured for this deployment."
Portfolio Brain needs the CLAUDE_API_KEY secret. Set it and redeploy callPortfolioBrain.
Portfolio Brain shows only the on-device summary
The app falls back to its local summary whenever callPortfolioBrain refuses. Check the function log: unauthenticated with an App Check message means App Check is not set up for that build (how); "Portfolio Brain is a PRO feature" or "needs a paid PRO plan" means the user has no active paid PRO (a free trial does not include Portfolio Brain); "today's limit" means a daily cap was reached (limits).
bootstrapFirstAdmin answers "Bootstrap is disabled until the ADMIN_SETUP_TOKEN secret …"
Set ADMIN_SETUP_TOKEN to a random value of 24 characters or more, redeploy bootstrapFirstAdmin, and send the same value as setupToken (how). "A valid setup token or the owner account is required" means the value you sent does not match.