Skip to content

Troubleshooting

This guide covers the most common issues encountered during development, deployment, and production operation of Nutri-E.


Table of Contents


iOS Build Errors

Missing Info.plist values at build time

Symptom: Build fails with Info.plist file is missing or key not found errors.

Cause: Info.plist is generated from Info.plist.template and requires environment variables injected by CI. Running a local build without those vars causes missing keys.

Fix:

# Copy and fill in the template
cp ios/Nutri-E/Info.plist.template ios/Nutri-E/Nutri-E/Info.plist
# Fill OPENAI_WORKER_URL, DSLD_WORKER_URL, etc. with sandbox values
Never commit the filled Info.plist — it contains environment-specific URLs.


GoogleService-Info.plist not found

Symptom: Build error: Could not find the file 'GoogleService-Info.plist'.

Cause: Firebase config file is .gitignored. Required for any build that uses Firebase (Crashlytics, Analytics).

Fix: 1. Download from Firebase Console → Nutri-E project → iOS app 2. Place at ios/Nutri-E/Nutri-E/GoogleService-Info.plist


HealthKit entitlement build error

Symptom: Entitlements file do not match or HealthKit capability missing in provisioning profile.

Cause: The entitlements file (Nutri-E.entitlements) declares com.apple.developer.healthkit = true. If the provisioning profile doesn't include HealthKit, signing fails.

Fix: - Ensure the provisioning profile (App Store Connect → Certificates, Identifiers & Profiles) has HealthKit enabled for no.invotek.NutriE - Regenerate profile if needed, then download and update in Xcode


Package.resolved conflicts / SPM cache issues

Symptom: error: package manifest loading failed, duplicate package errors, or resolution timeout.

Fix:

# Reset SPM cache
cd ios/Nutri-E
rm -rf ~/Library/Caches/org.swift.swiftpm
rm -rf .build
# Reopen Xcode and let it resolve packages


413 Request Too Large when reading images

Symptom: Claude Code crashes mid-task with API error when reading PNG files.

Cause: Read tool has a ~1-2 MB limit. Marketing screenshots and HEIC exports often exceed this.

Fix:

# Resize before reading
convert large-image.png -resize 800x800\> -quality 85 large-image.png
# Or check size first
ls -lh image.png
sips -g pixelWidth -g pixelHeight image.png


Xcode Cloud Failures

Build triggers but doesn't reach TestFlight

Symptom: Xcode Cloud shows green but no build appears in TestFlight.

Common causes and fixes:

  1. Missing export options — Check ios/Nutri-E/ExportOptions.plist exists and has correct provisioning profile names
  2. Build number not incrementing — Xcode Cloud manages its own build counter (separate from local project version). Check Xcode Cloud → Settings → Build Number for the current counter
  3. Missing ci_pre_xcodebuild.sh — The script at ios/ci_scripts/ci_pre_xcodebuild.sh installs dependencies and injects secrets. Ensure it's executable: chmod +x ios/ci_scripts/ci_pre_xcodebuild.sh

Xcode Cloud can't find GoogleService-Info.plist

Symptom: Cloud build fails with Firebase config missing.

Fix: The ci_pre_xcodebuild.sh script must write this file from a CI environment variable (GOOGLE_SERVICE_INFO_PLIST_BASE64). Set this secret in Xcode Cloud → Workflow → Environment Variables as a base64-encoded version of the file:

base64 -i GoogleService-Info.plist | pbcopy  # copy to clipboard


ARC runner (arc-linux-nutri-e) not picking up jobs

Symptom: GitHub Actions workflow shows "Waiting for a runner" indefinitely.

Fix: 1. Check ARC runner health in tablez-gitops cluster: kubectl get pods -n arc-runners 2. If pod is CrashLoopBackOff, restart: kubectl rollout restart deployment arc-linux-nutri-e -n arc-runners 3. Verify github-pat secret in arc-runners namespace hasn't expired


deploy-workers.yml deploys sandbox but skips production

Symptom: Sandbox deploys succeed; production stage shows as skipped.

Cause: Production deploy requires sandbox health check to pass first (sequential stages in the workflow). A failing sandbox health check blocks production.

Fix: Check the sandbox health check step output in the failed run. Common issue: worker deployed but KV namespace IDs in wrangler.toml don't match the Cloudflare account's actual namespace IDs.


Cloudflare Worker Deployment

wrangler deploy fails with authentication error

Symptom: ✘ [ERROR] Authentication error.

Fix:

wrangler login  # opens browser OAuth flow
# Or use API token:
export CLOUDFLARE_API_TOKEN=<your-token>
wrangler deploy


KV namespace ID mismatch

Symptom: Worker deploys but returns KV namespace not found at runtime, or wrangler deploy fails with namespace binding error.

Cause: wrangler.toml contains hardcoded namespace IDs that belong to a specific Cloudflare account. IDs differ between accounts and environments.

Fix:

# List your actual namespace IDs
wrangler kv namespace list

# Update wrangler.toml with your IDs:
# [[kv_namespaces]]
# binding = "SUBSCRIPTIONS"
# id = "<your-actual-id>"
# preview_id = "<your-sandbox-id>"


Worker secret not set after deploy

Symptom: Worker returns 401 Unauthorized or DEVICE_SALT is undefined.

Cause: Secrets must be set separately from wrangler deploy. They aren't in wrangler.toml.

Fix:

cd cloudflare-worker-openai
wrangler secret put OPENAI_API_KEY      # prompts for value
wrangler secret put DEVICE_SALT
wrangler secret put REVENUECAT_WEBHOOK_SECRET  # apple worker only

# Verify secrets are set
wrangler secret list


Deploying to wrong environment (sandbox vs production)

Symptom: TestFlight app hits production quota limits, or App Store users get sandbox rates.

Fix: Always specify the environment explicitly:

wrangler deploy --env sandbox-v3   # TestFlight
wrangler deploy --env v3           # App Store

Check which URL the iOS app is using: sandbox builds use nutrie-*-sandbox-v3.invotekas.workers.dev, production uses nutrie-*-v3.invotekas.workers.dev.


Worker Runtime Errors

401 — Invalid device signature

Symptom: API calls from app return {"error": "Invalid device signature"}.

Cause: Device auth uses HMAC-SHA256(deviceId + timestamp + DEVICE_SALT). Signature mismatch means either wrong salt, clock skew >60s, or device ID format mismatch.

Debugging steps: 1. Check that DEVICE_SALT secret matches between sandbox and production workers 2. Verify device clock isn't skewed — the worker rejects requests where |now - timestamp| > 60s 3. In the app: Settings → Developer Tools → Device Info → copy Device ID and compare with what the worker receives in logs (wrangler tail)


429 — Rate limit exceeded

Symptom: App returns rate limit error mid-session.

Cause: KV-based daily quota (OpenAI: 10–500 req/day; DSLD: 100–unlimited, depending on subscription tier). Quota resets at midnight UTC.

Debugging:

# Check quota for a device
wrangler kv key get --namespace-id=<RATE_LIMIT_ID> "rate_limit_openai_<deviceId>"
wrangler kv key get --namespace-id=<RATE_LIMIT_ID> "rate_limit_dsld_<deviceId>"

Reset for testing:

wrangler kv key delete --namespace-id=<RATE_LIMIT_ID> "rate_limit_openai_<deviceId>"


OpenAI worker returns 503 — Vision AI service temporarily unavailable

Symptom: Label scanning or food analysis fails with service error.

Cause: OpenAI API error (quota, outage, or invalid key).

Debugging:

# Tail worker logs
wrangler tail nutrie-openai-worker-v3

# Check OpenAI status
open https://status.openai.com

# Verify API key is valid
curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY" | jq '.data | length'

If key is valid but quota exceeded, check OpenAI billing.


Apple webhook worker returns JWT verification failed

Symptom: Subscription purchases aren't being recorded. Worker logs show JWT signature verification FAILED.

Cause: The Apple webhook worker verifies JWT signatures against Apple's root CA. Failures happen when: 1. The payload format changed (Apple updated their JWT structure) 2. REVENUECAT_WEBHOOK_SECRET doesn't match what's configured in App Store Connect / RevenueCat 3. Clock skew on the worker (rare, Cloudflare handles NTP)

Fix: 1. Confirm RevenueCat is set to send webhooks to the correct worker URL (sandbox vs production) 2. Re-check REVENUECAT_WEBHOOK_SECRET matches between RevenueCat dashboard and wrangler secret 3. Test with the included script: bash cloudflare-worker-apple/test-apple-webhook.sh


Subscription / RevenueCat

User purchased but app still shows free tier

Cause: The subscription flow is asynchronous. Apple sends the webhook to Cloudflare ~1 minute after purchase. Until then, the SUBSCRIPTIONS KV has no entry for the device.

Expected behaviour: The StoreKitManager updates local AIUsageTracker immediately for UI feedback, but the worker enforces quotas from KV. Wait 1–2 minutes and retry.

If still not resolved after 5 minutes: 1. Check worker logs for the device's purchase event: wrangler tail nutrie-apple-webhook-worker-v3 2. Verify the device ID was passed correctly as appAccountToken during purchase 3. Check SUBSCRIPTIONS KV directly:

wrangler kv key get --namespace-id=<SUBSCRIPTIONS_ID> "<deviceId>"


Subscription shows active in RevenueCat but not in app

Cause: RevenueCat and the Cloudflare KV are independent systems. RevenueCat is the source of truth for billing; SUBSCRIPTIONS KV is the source of truth for the API workers.

Fix: Manually inspect and if needed, manually set the KV entry:

# Check what's in KV
wrangler kv key get --namespace-id=<SUBSCRIPTIONS_ID> "<deviceId>"

# Manually set subscription (temporary fix while investigating root cause)
wrangler kv key put --namespace-id=<SUBSCRIPTIONS_ID> "<deviceId>" \
  '{"plan":"pro","expiresAt":"2026-12-31T00:00:00Z","updatedAt":"2026-03-15T00:00:00Z"}'


Cancellation not being processed

Cause: Cancellation events come through the Apple webhook. The worker moves the record to SUBSCRIPTIONS_DELETED KV and removes from SUBSCRIPTIONS.

Debugging: 1. Check SUBSCRIPTIONS_DELETED KV for the device record 2. If missing, the webhook may not have arrived — check App Store Connect → Subscriptions → server notifications log 3. Ensure the webhook URL in App Store Connect points to the correct worker environment (sandbox URL for sandbox, production URL for production)


Product IDs not loading in SubscriptionView

Symptom: Subscription page is blank or shows loading spinner indefinitely.

Cause: StoreKit 2 can't find the products. Common in simulator or when products are in Ready to Submit state (not yet approved).

Fix: 1. Test on a real device with a Sandbox Apple ID 2. Confirm product IDs in App Store Connect match exactly: no.invotek.nutrie.basic_monthly, pro_monthly, premium_monthly, ultimate_monthly 3. Products must be in Approved or Ready for Sale state (not Missing Metadata) 4. In simulator, use StoreKit configuration file for local testing


HealthKit Permissions

HealthKit data not saving after user grants permission

Symptom: User taps Allow in HealthKit permission sheet, but no data appears in Health app.

Cause: HealthKit permission is granted per-type. The entitlements file has com.apple.developer.healthkit.access as an empty array (no pre-declared types), meaning all types require runtime authorisation.

Fix (in code): Confirm HealthKitService.requestAuthorization() is called before any write attempt and that the result is checked:

guard await healthKitService.requestAuthorization() else {
    // Handle denial
    return
}


HealthKit permission prompt not appearing

Symptom: App never shows the HealthKit permission sheet.

Causes: 1. HealthKit not available on the device (iPad without health data, simulator) 2. Permission was previously denied — iOS doesn't re-prompt; user must go to Settings

Fix: 1. Check HKHealthStore.isHealthDataAvailable() before calling requestAuthorization() 2. If previously denied: Settings → Privacy & Security → Health → Nutri-E → re-enable 3. On simulator: HealthKit is not available — test on a real device


aps-environment entitlement mismatch

Symptom: Push notifications work on TestFlight but not in development builds, or vice versa.

Cause: Nutri-E.entitlements has aps-environment = development. Production (App Store / TestFlight) builds must use production.

Fix: Xcode automatically uses the correct environment when signing with a distribution provisioning profile. For local dev builds, development is correct. If push isn't working in a distribution build, verify the provisioning profile is a distribution (not development) profile.


Device Auth / Token Issues

All API calls return 401 after app update

Symptom: After updating the app, every request returns 401.

Cause: DEVICE_SALT was rotated on the worker after the previous release. Old app version uses the old signature scheme.

Fix: 1. Check that DEVICE_SALT matches between the version the app was compiled against and the deployed worker 2. If salt was intentionally rotated: old app versions will need to update. Post a forced-update notice via RevenueCat or App Store phased release


Device ID changes between sessions

Symptom: User loses their quota reset or subscription state after reinstalling.

Cause: Device ID is stored in UserDefaults (not Keychain). Reinstall wipes UserDefaults.

Known limitation: This is by design for the current implementation. A returning user after reinstall gets a fresh device ID and starts at free tier until the next Apple webhook fires and links by appAccountToken.


X-Device-Token vs X-Device-ID header confusion

Symptom: Worker logs show auth failures even with correct credentials.

Cause: Workers accept both X-Device-Token (preferred, rotation-friendly) and X-App-ID + X-Device-ID (legacy). The iOS app must use one scheme consistently — mixing headers causes auth failure.

Fix: Check which header scheme the current iOS build sends (search codebase for X-Device-Token). Ensure the worker's expected scheme matches.


Escalation

If you can't resolve an issue after following the steps above:

Issue type Contact / Resource
Cloudflare Worker / KV / D1 Cloudflare status · Cloudflare Discord
OpenAI API outage OpenAI status · OpenAI platform dashboard
Apple webhook / App Store Connect Apple developer forums · App Store Connect → Agreements, Tax, Banking
RevenueCat subscription data RevenueCat dashboard → customer lookup by device ID
HealthKit / StoreKit 2 bugs Apple TSI (Technical Support Incident) via developer account
Xcode Cloud build issues Xcode Cloud dashboard → build logs · Apple Developer Forums
CI runner down (ARC) Check tablez-gitops cluster: kubectl get pods -n arc-runners · contact Stig-Johnny
Security issue (key leak, exploit) Stig-Johnny directly — do not file a public GitHub issue