# Troubleshooting

Start with the exact failing boundary: account/session, app identity, store credential, receipt verification, access mapping, presentation, delivery, or reporting. Repeating every setup step at once makes the cause harder to isolate.

## Account, invitation, and permission failures

| Symptom | Check | Next action |
| --- | --- | --- |
| Sign-in fails | Correct account and deployment; password rules do not change an existing password | Use the intended account or password reset |
| Verification/reset link fails | Latest link, expiry, correct browser/account | Request a new link and continue locally |
| Invitation email mismatch | The signed-in email versus the invited email | Use the invited account |
| View access / 403 | Project role and email verification | Ask an owner/admin for the needed role |
| Shared analysis cannot open | Recipient's project/app membership | Grant legitimate membership; a URL alone grants none |
| Resource not found after switching | Current project/app versus the old link | Reselect scope and reopen the resource |

Default link lifetimes are 24 hours for verification, 30 minutes for reset, and seven days for invitations. Missing email can also indicate gateway configuration or delivery failure. [Accounts](/docs/accounts-and-projects).

## A store credential will not save

For Apple, use the In-App Purchase `.p8`, its correct Key ID, and Issuer ID. The filename can populate Key ID but cannot prove the key is the right kind or belongs to the intended team. In CLI setup, check the selected provider and the existence of the bundle record.

For Google, choose the service-account key JSON, not a Desktop OAuth client JSON. Check its token URI and account email, the package-level Play grants, and API enablement. Organization policies can legitimately prevent key creation.

Managed credential storage requires the installation's encryption-key configuration. A 409 can indicate unavailable managed storage rather than a bad store password. The credential validator's file limits and the hosted API envelope limit are different; see [Limits](/docs/limits-and-release-status) for large uploads.

## Apple purchase fails before reaching CherrySub

If StoreKit itself returns an error (including an unexplained 3504), first determine whether it happened before any signed transaction was produced or any receipt request reached the API. Inspect tester/build eligibility, agreements, product availability, bundle/app IDs, network, and storefront logs. A pre-purchase store error is not repaired by fabricating a JWS or disabling backend verification.

For reproducible store-side failures, collect non-secret timestamps, product/bundle IDs, build information, and the store error domain/code for the appropriate Apple support path. Do not assume every 3504 error has the same cause.

## Purchase exists but access is inactive

1. Confirm the selected app and the broker's App User ID.
2. Find the verified transaction and its current owner.
3. Check the exact product/platform access mapping and its grant mode.
4. Compare expiry and refund/revocation evidence.
5. Inspect other grant contributions to the same access level.
6. Refresh the subscriber response and clear stale application-owned access UI.

A product set controls presentation, not access. A mapping edit does not retroactively rewrite all past evidence. Restore can change one purchase's ownership without merging identities. [Access](/docs/access-levels) · [Ownership](/docs/customer-identity).

## Receipt/API errors

| Status | Common meaning | Handling |
| --- | --- | --- |
| 400 / 422 | Bad request shape or store evidence/identity validation | Correct the payload or configuration; do not blindly retry unchanged invalid data |
| 401 | Missing, expired, revoked, cross-app, or invalid credential/token | Refresh once where supported; verify broker subject and deployment |
| 403 | Wrong key kind or insufficient hosted project permission | Use the documented trust boundary |
| 404 | Missing scoped resource or a not-yet-created customer alias | Confirm scope; do not treat a missing subscriber as active |
| 409 | Configuration or lifecycle conflict | Inspect current state before retrying |
| 413 | Request too large | Use the supported payload, not an entire unrelated receipt bundle |
| 429 | Throttled | Respect Retry-After and reduce concurrent retries |
| 503 | Verifier/dependency/configuration unavailable | Investigate readiness and retry transient failures with bounds |

Runtime API errors use an `error` field. Hosted account/dashboard errors use `message` and sometimes a machine-readable `code`. The [API references](/docs/api-reference) and [hosted API](/docs/hosted-api) distinguish them.

## Notifications or webhooks are missing

Apple notifications require the V2 URL and matching signed app identity. Google RTDN requires an authenticated Pub/Sub push audience/email and a successful authoritative purchase read for purchase notifications. A test notification alone does not prove paid purchase access.

For outbound webhooks, confirm the endpoint is enabled, its filter matches a supported derived event, and the receiver returns 2xx. Inspect the head-of-line failed delivery. Verify signatures against raw bytes and the current/overlap secrets. Fix the receiver before replay; replay preserves its message ID.

## Charts disagree with another report

Check app, environment, currency, UTC range, resolution, current-snapshot versus period series, and cohort anchor/window/maturity. Standard refund cohorts use purchase dates; realized LTV uses in-lifetime refund timestamps. Customer activity is not application engagement DAU. Net is not store proceeds after taxes/commission.

Missing history or accounting enrichment can leave incomplete financial data. Run rate uses observed effective duration, and partial-month movement buckets have preview limitations. See [Metrics](/docs/metrics) before escalating a supposed mismatch.

## CLI failure after creating a key

The CLI prints the newly created key's ID when upload fails. Check `cherrysub stores status APP_UUID` first: a network timeout can happen after save. Revoke only an unused new key in the store console; old keys were retained. Apple private keys may not be downloadable again.

For a busy CLI profile, wait for the current command. Remove the reported lock file only after verifying no command is running. Use separate signed-in profiles for concurrent jobs. [CLI recovery](/docs/cli).
