
Gpc Troubleshooting
- 26 installs
- 1 repo stars
- Updated August 1, 2026
- yasserstudio/gpc-skills
Helps with ai & agent building tasks.
About
gpc-troubleshooting is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- gpc-troubleshooting
- AI & Agent Building
- AI-coding skill
Gpc Troubleshooting by the numbers
- 26 all-time installs (skills.sh)
- Ranked #9,702 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yasserstudio/gpc-skills --skill gpc-troubleshootingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 26 |
|---|---|
| repo stars | ★ 1 |
| Last updated | August 1, 2026 |
| Repository | yasserstudio/gpc-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
gpc-troubleshooting
Unified debugging guide for all GPC errors, exit codes, and common issues.
When to use
- GPC command fails with an error code
gpc doctorreports issues- CI pipeline fails with a GPC step
- Unexpected behavior or output
- Need to interpret exit codes
- Need to enable verbose/debug output
Inputs required
- Error message or exit code — what GPC reported
- Command that failed — the full command that was run
- Environment — local vs CI, OS, Node.js version
Procedure
0. Quick diagnosis
# Check GPC health
gpc doctor
# Get version info
gpc --version
# Run failing command with verbose output
GPC_DEBUG=1 gpc <failing-command>
# Get error as JSON for parsing (also: --output csv, --output tsv since v0.9.68)
gpc <failing-command> --jsonRead: references/exit-codes.md for the complete exit code reference.
1. Exit codes
| Code | Category | Meaning |
|---|---|---|
| 0 | Success | Command completed successfully |
| 1 | Config | Configuration error (missing .gpcrc.json, invalid fields) |
| 2 | Usage | Invalid arguments or flags |
| 3 | Auth | Authentication failure (expired token, invalid key, no credentials) |
| 4 | API | Google Play API error (403, 404, 408, 409, rate limit) |
| 5 | Network | Connection failure, DNS error, timeout |
| 6 | Threshold | Vitals threshold breached (used in CI gating) |
| 10 | Plugin | Plugin permission validation error |
2. Authentication errors (exit code 3)
Read: references/error-catalog.md for the full error catalog.
| Error | Cause | Fix |
|---|---|---|
AUTH_FAILED | Invalid or corrupted credentials | Re-run gpc auth login --service-account <key.json> |
AUTH_EXPIRED | Token expired and refresh failed | Re-authenticate; check network/proxy |
AUTH_NO_CREDENTIALS | No auth configured | Run gpc auth login or set GPC_SERVICE_ACCOUNT |
AUTH_INVALID_KEY | Malformed service account JSON | Re-download key from Google Cloud Console |
AUTH_KEYCHAIN_ERROR | OS keychain access denied | Grant keychain access or use env var auth |
# Check current auth status
gpc auth status
# Re-authenticate
gpc auth login --service-account ~/path/to/key.json
# Bypass keychain with env var
export GPC_SERVICE_ACCOUNT=$(cat ~/path/to/key.json)3. API errors (exit code 4)
| Error | HTTP | Cause | Fix |
|---|---|---|---|
API_FORBIDDEN | 403 | Insufficient permissions | Grant required roles in Play Console |
API_NOT_FOUND | 404 | App, track, or resource doesn't exist | Verify package name, track name, or resource ID |
API_CONFLICT | 409 | Edit already in progress | Wait and retry; another edit may be open |
API_RATE_LIMITED | 429 | Too many requests | GPC auto-retries; increase GPC_BASE_DELAY |
API_REQUEST_TIMEOUT | 408 | Request timed out | GPC auto-retries with exponential backoff |
API_SERVER_ERROR | 5xx | Google server issue | Retry later; check Google status dashboard |
EDIT_CONFLICT | 409 | Concurrent edit from another tool/user | Only one edit at a time; check if Fastlane or Play Console has an open edit |
API_DUPLICATE_VERSION_CODE | 409 | Version code already uploaded | Increment versionCode in build.gradle and rebuild |
API_VERSION_CODE_TOO_LOW | 400 | Version code lower than current | Version code must increase per track |
API_PACKAGE_NAME_MISMATCH | 400 | applicationId doesn't match target app | Verify applicationId matches target app |
API_APP_NOT_FOUND | 404 | App not in developer account | Verify package name and developer account |
API_INSUFFICIENT_PERMISSIONS | 403 | Service account missing permissions | Grant required roles in Play Console → Settings → API access |
API_CHANGES_NOT_SENT_FOR_REVIEW | 400/403 | App has rejected update, requires review flag | Add --changes-not-sent-for-review flag to the command |
API_CHANGES_ALREADY_IN_REVIEW | 400 | Changes already in review, new commit would silently cancel | Use --error-if-in-review to prevent silent cancellation |
API_EDIT_EXPIRED | 410 | The open edit session has expired (edits expire after ~30 minutes of inactivity) | GPC now includes a clear API_EDIT_EXPIRED message with a suggestion to retry the command. The command will automatically create a fresh edit on retry. |
API_ROLLOUT_DECREASE_FORBIDDEN | 400 | Staged rollout percentage can only be increased, not decreased | To stop a rollout, use gpc releases rollout halt --track production. To continue, use gpc releases rollout increase with a higher percentage. |
# Check if an edit is stuck
gpc apps list --json
# API errors include suggestion field
gpc releases upload app.aab --track beta --json 2>&1 | jq '.error'4. Network errors (exit code 5)
| Error | Cause | Fix |
|---|---|---|
NETWORK_ERROR | Connection failed | Check internet; check proxy settings |
NETWORK_TIMEOUT | Request timed out | Increase GPC_TIMEOUT (default 30000ms) |
NETWORK_DNS | DNS resolution failed | Check DNS settings; try Google DNS (8.8.8.8) |
NETWORK_SSL | SSL/TLS handshake failed | Set GPC_CA_CERT for custom CA; check proxy |
# Increase timeout for large uploads
export GPC_TIMEOUT=120000
# Configure retry behavior
export GPC_MAX_RETRIES=5
export GPC_BASE_DELAY=2000
export GPC_MAX_DELAY=30000
# Corporate proxy
export HTTPS_PROXY=http://proxy.corp:8080
# Custom CA certificate
export GPC_CA_CERT=/path/to/ca-cert.pem5. Configuration errors (exit code 1)
| Error | Cause | Fix |
|---|---|---|
CONFIG_MISSING | No .gpcrc.json or env vars | Run gpc setup (v0.9.68+) or gpc config init |
CONFIG_INVALID | Malformed .gpcrc.json | Validate JSON syntax |
CONFIG_INVALID_JSON | Config file contains syntax errors (v0.9.80+) | Run `cat <file> \ |
CONFIG_INVALID_KEY | Key is empty, malformed, or a reserved name (v0.9.80+) | Use a valid alphanumeric profile/key name |
CONFIG_APP_MISSING | No app specified | Set with gpc config set app or --app flag |
Config precedence fix (v0.9.81+): Before v0.9.81, an active profile would silently win over GPC_SERVICE_ACCOUNT/GPC_APP env vars and the --service-account/--app flags. This was a bug. Since v0.9.81, the documented precedence is enforced: flags override env vars, env vars override the active profile, the active profile overrides defaults. If env vars appear to be ignored, check whether an active profile is set with gpc config list and either update or deactivate it.
# Initialize config
gpc config init
# Set required values
gpc config set app com.example.app
# Check current config
gpc config list6. Upload and release errors
| Error | Cause | Fix |
|---|---|---|
INVALID_BUNDLE | AAB is corrupted or wrong format | Rebuild the AAB; run gpc validate first |
VERSION_CODE_CONFLICT | Version code already used | Increment versionCode in build.gradle |
RELEASE_NOT_FOUND | No release on the specified track | Check track name; use gpc releases list --track <track> |
ROLLOUT_INVALID | Invalid rollout percentage | Use 0-100 (not 0.0-1.0); use --rollout 10 not --rollout 0.1 |
PROMOTE_NO_SOURCE | Source track has no release to promote | Upload to source track first |
UPLOAD_CHUNK_FAILED | Chunk could not be sent after retries | Check network; increase GPC_MAX_RETRIES or GPC_UPLOAD_TIMEOUT |
UPLOAD_NO_COMPLETION | All bytes sent but no completion response | Retry upload; check GPC_UPLOAD_TIMEOUT |
UPLOAD_INITIATE_FAILED | Session initiation failed | Check auth and permissions; retry |
UPLOAD_NO_SESSION_URI | No Location header in initiation response | API error; retry or check service account permissions |
UPLOAD_SESSION_NOT_FOUND | Session expired (404) | Start a new upload session |
UPLOAD_SESSION_EXPIRED | Session gone (410) | Start a new upload session |
UPLOAD_INVALID_CHUNK_SIZE | Chunk size not multiple of 256 KB | Set GPC_UPLOAD_CHUNK_SIZE to a multiple of 262144 (256 KB) |
EDIT_VALIDATE_FAILED | Transient validate/commit failure after upload | Auto-retried with multi-retry guard (15s, 30s, 45s) since v0.9.77; if persistent, check bundle status |
REVIEW_SKIPPED | Internal track commit completed without entering Google review queue | Expected behavior. The internal track does not require review. GPC sets reviewSkipped: true in structured JSON output (v0.9.79+) to confirm the commit went through immediately. |
# Validate before uploading
gpc validate app.aab
# Check existing releases
gpc releases list --track internal
gpc releases list --track beta
# Preview upload
gpc releases upload app.aab --track beta --dry-runCommit rejection: reviewPending structured output (v0.9.79+)
When edits.commit is rejected because Google requires review, the --json output includes a structured result instead of a plain error:
{
"reviewPending": true,
"nextStep": "Your changes are under Google review. Check Play Console for status or use --changes-not-sent-for-review to bypass review for non-reviewed tracks."
}CI pipelines can key on reviewPending === true to decide whether to wait, notify, or exit. The nextStep field always contains human-readable guidance on what to do next.
7. Vitals threshold breach (exit code 6)
Exit code 6 is not an error — it's an intentional signal that a vitals metric exceeded the threshold. Used for CI gating.
# This exits 6 if crash rate > 2.0%
gpc vitals crashes --threshold 2.0
# Check the actual value
gpc vitals crashes --json | jq '.crashRate'
# In CI, use exit code to gate promotion
gpc vitals crashes --threshold 1.5 && gpc releases promote --from beta --to production8. Plugin errors (exit code 10)
| Error | Cause | Fix |
|---|---|---|
PLUGIN_INVALID_PERMISSION | Third-party plugin declares unknown permission | Check valid permissions in plugin-sdk docs |
| Plugin not loading | Not in config or not approved | Add to plugins and approvedPlugins in .gpcrc.json |
| Plugin error in hook | Bug in plugin handler | Check plugin logs; onError/API hooks swallow errors |
Changelog generation errors (v0.9.61+)
| Code | Meaning | Fix |
|---|---|---|
CHANGELOG_NO_TAG | No v* git tag found, --from not passed | Create a tag (git tag v0.0.1) or pass --from <ref> |
CHANGELOG_BAD_REF | --from or --to ref doesn't exist | Run git rev-parse --verify <ref> to check |
CHANGELOG_LOCALES_REQUIRED | --target play-store passed without --locales (v0.9.62+) | Pass --locales en-US,fr-FR or --locales auto |
CHANGELOG_LOCALES_INVALID | One or more --locales are not valid BCP 47 (v0.9.62+) | Use Play Store-supported codes like en-US, fr-FR, de-DE |
CHANGELOG_LOCALES_AUTO_NO_APP | --locales auto without an authenticated client + app (v0.9.62+) | Pass --app <package> or set config.app, check credentials |
CHANGELOG_FETCH_FAILED | GitHub API unreachable or returned an error (v0.9.80+) | Check network; view changelog at the docs site |
CHANGELOG_VERSION_NOT_FOUND | Requested version not found in GitHub releases (v0.9.80+) | Run gpc changelog --limit 10 to see available versions |
WATCH_WEBHOOK_FAILED | Webhook endpoint returned non-2xx (v0.9.80+) | Check the webhook URL and server status |
CHANGELOG_LOCALES_EMPTY | --locales auto returned zero locales (v0.9.62+) | Create at least one Play Store listing, or pass explicit --locales |
RELEASE_NO_DRAFT | --apply found no draft release on the target track (v0.9.64+) | Create a draft release first (gpc releases upload --status draft) |
BUNDLE_PROCESSING_TIMEOUT | AAB upload completed but bundle not processed within ~86s (v0.9.64+, extended v0.9.77) | Retry the upload, or use --status draft and commit later; if persistent, check bundle size and Google's server status |
9. Debug mode
Enable verbose output for any command:
# Debug environment variable
GPC_DEBUG=1 gpc releases upload app.aab --track beta
# JSON output for machine parsing
gpc releases upload app.aab --track beta --json
# Combine for maximum detail
GPC_DEBUG=1 gpc releases upload app.aab --track beta --json 2>debug.log10. Retryable HTTP status codes
GPC automatically retries the following HTTP status codes with exponential backoff:
- 408 — Request Timeout
- 429 — Too Many Requests (rate limited)
- 5xx — Server errors (500, 502, 503, etc.)
Configure retry behavior:
export GPC_MAX_RETRIES=5 # Default: 5
export GPC_BASE_DELAY=1000 # Initial delay in ms
export GPC_MAX_DELAY=15000 # Max delay in ms
export GPC_UPLOAD_TIMEOUT=300000 # Upload timeout in ms (5 min)Verification
gpc doctorpasses all checks- Failing command now succeeds or shows a clear, actionable error
- Exit code matches the expected category
--jsonoutput includeserror.code,error.message, anderror.suggestion
Failure modes / debugging
| Symptom | Likely Cause | Fix |
|---|---|---|
gpc doctor fails on auth | Credentials not configured | Run gpc setup (v0.9.68+) or gpc auth login |
gpc doctor fails on API | Service account lacks API access | Enable Google Play Developer API in GCP |
gpc doctor quota warning | >80% of daily or per-minute API quota used | Reduce request frequency or request quota increase from Google (v0.9.71+) |
gpc doctor plugin error | A configured plugin fails to load | Check plugin package version, reinstall, or remove from config (v0.9.71+) |
gpc doctor --verify mismatch | Local keystore differs from Play signing cert | Register local key in Play Console or use Play App Signing (v0.9.75+) |
| All commands timeout | Network/proxy issue | Check HTTPS_PROXY, GPC_CA_CERT, GPC_TIMEOUT |
| Commands work locally, fail in CI | Missing env vars in CI | Set GPC_SERVICE_ACCOUNT and GPC_APP in CI secrets; run gpc setup --auto (v0.9.68+) |
Env vars GPC_SERVICE_ACCOUNT / GPC_APP seem to be ignored | Active profile overriding env vars (pre-v0.9.81 bug) | Upgrade to v0.9.81+. Check active profile with gpc config list; env vars and flags now correctly override the profile. |
JSON output has no suggestion | Unexpected error type | File a bug — all errors should have suggestions |
Related skills
- gpc-setup — initial auth and config setup
- gpc-ci-integration — CI-specific troubleshooting
- gpc-vitals-monitoring — understanding threshold breaches
- gpc-plugin-development — debugging plugin issues
{
"skill_name": "gpc-troubleshooting",
"evals": [
{
"id": 1,
"prompt": "My gpc releases upload command is failing with exit code 4 and the error says API_FORBIDDEN. I'm using a service account and it works for gpc releases list but not upload. What's going on?",
"expected_output": "Diagnoses insufficient permissions and shows how to fix in Play Console",
"files": [],
"expectations": [
"Explains exit code 4 is an API error",
"Identifies API_FORBIDDEN as a permissions issue",
"Explains that list (read) and upload (write) require different permission levels",
"Suggests granting 'Release to production' or 'Release manager' role in Play Console",
"Shows gpc auth status and gpc doctor for verification"
]
},
{
"id": 2,
"prompt": "Our CI pipeline keeps failing with exit code 5 and NETWORK_TIMEOUT. The AAB is about 150MB. It works sometimes but fails maybe 40% of the time. We're on a shared GitLab runner.",
"expected_output": "Configures timeout and retry environment variables for large uploads on flaky networks",
"files": [],
"expectations": [
"Explains exit code 5 is a network error",
"Suggests increasing GPC_TIMEOUT to 120000 or higher for 150MB uploads",
"Suggests increasing GPC_MAX_RETRIES to 5",
"Mentions GPC_BASE_DELAY and GPC_MAX_DELAY for backoff tuning",
"Shows the env vars in GitLab CI YAML format"
]
},
{
"id": 3,
"prompt": "I'm getting exit code 6 from gpc vitals crashes --threshold 1.5 in our GitHub Actions workflow. Is this a bug? The command seems to run fine but the step shows as failed.",
"expected_output": "Explains exit code 6 is an intentional threshold breach signal, not a bug",
"files": [],
"expectations": [
"Explains exit code 6 means threshold was breached (not an error)",
"Shows that the crash rate exceeded the 1.5% threshold",
"Suggests gpc vitals crashes --json to see the actual crash rate value",
"Shows how to use exit code 6 in CI with continue-on-error or conditional steps",
"Explains this is designed for CI gating — blocking promotion when vitals are bad"
]
},
{
"id": 4,
"prompt": "I'm getting 400 INVALID_ARGUMENT when I run 'gpc vitals lmk'. All other vitals commands work fine. What's wrong?",
"expected_output": "Diagnoses the LMK metrics API requirement and shows the correct command",
"files": [],
"expectations": [
"Explains that 'lmk' (Low Memory Kills) requires different metrics and DAILY aggregation",
"Confirms that gpc vitals lmk uses a dedicated endpoint (stuckBackgroundWakelockRateMetricSet or lmkRateMetricSet)",
"Shows the correct usage: gpc vitals lmk",
"Mentions that other vitals use HOURLY aggregation but lmk requires DAILY",
"Suggests using gpc vitals overview for a combined dashboard if lmk is still failing"
]
},
{
"id": 5,
"prompt": "gpc quota usage is showing '[object Object]' instead of actual numbers. I'm on v0.9.35 or earlier. Is this a known bug?",
"expected_output": "Identifies the fixed bug and advises upgrading",
"files": [],
"expectations": [
"Confirms this was a known bug fixed in v0.9.36 where quota usage displayed as [object Object]",
"Advises upgrading: npm update -g @gpc-cli/cli or gpc update",
"Shows what correct quota usage output looks like",
"Explains the bug was in serializing the usage object to string"
]
},
{
"id": 6,
"prompt": "My release upload keeps failing with 'API_EDIT_EXPIRED'. I think it's because a previous gpc command crashed mid-edit. How do I recover?",
"expected_output": "Explains the stale edit issue and shows how GPC auto-retries, plus manual recovery",
"files": [],
"expectations": [
"Explains that edit IDs expire if a session crashes before committing",
"Notes that GPC v0.9.35+ auto-retries with a fresh edit on FAILED_PRECONDITION/API_EDIT_EXPIRED",
"If still failing, suggests using Play Console UI to discard any pending edits",
"Advises upgrading to v0.9.35+ which has the withFreshEdit() auto-retry fix",
"Shows gpc releases upload as the command that triggers edit creation"
]
}
]
}
Error Catalog
All known GPC error codes with causes and fixes.
Authentication errors (exit code 3)
| Code | Message | Fix |
|---|---|---|
AUTH_FAILED | Authentication failed | Re-run gpc auth login --service-account <key> |
AUTH_EXPIRED | Token expired | Re-authenticate; check network if refresh fails |
AUTH_NO_CREDENTIALS | No credentials found | Run gpc auth login or set GPC_SERVICE_ACCOUNT |
AUTH_INVALID_KEY | Malformed service account JSON | Re-download from Google Cloud Console |
AUTH_KEYCHAIN_ERROR | Cannot access OS keychain | Grant access or use GPC_SERVICE_ACCOUNT env var |
AUTH_SCOPE_DENIED | OAuth scope not granted | Re-authorize with required scopes |
API errors (exit code 4)
| Code | HTTP | Message | Fix |
|---|---|---|---|
API_FORBIDDEN | 403 | Insufficient permissions | Grant required roles in Play Console |
API_NOT_FOUND | 404 | Resource not found | Verify app, track, or resource ID |
API_CONFLICT | 409 | Edit conflict | Wait; another edit may be open |
API_RATE_LIMITED | 429 | Rate limit exceeded | Auto-retries; increase GPC_BASE_DELAY |
API_REQUEST_TIMEOUT | 408 | Request timeout | Auto-retries with exponential backoff |
API_SERVER_ERROR | 5xx | Google server error | Retry later |
API_BAD_REQUEST | 400 | Invalid request data | Check JSON payload structure |
API_GONE | 410 | Resource no longer available | Resource was deleted or deprecated |
API_DUPLICATE_VERSION_CODE | 409 | Version code already uploaded | Increment versionCode in build.gradle and rebuild |
API_VERSION_CODE_TOO_LOW | 400 | Version code lower than current | Version code must increase per track |
API_PACKAGE_NAME_MISMATCH | 400 | Package name doesn't match | Verify applicationId matches target app |
API_APP_NOT_FOUND | 404 | App not in developer account | Verify package name and developer account |
API_INSUFFICIENT_PERMISSIONS | 403 | Service account missing permissions | Grant required roles in Play Console → Settings → API access |
API_BUNDLE_TOO_LARGE | 400 | AAB or APK exceeds size limit | AAB max 2 GB, APK max 1 GB |
API_INVALID_BUNDLE | 400 | Corrupt or improperly signed bundle | Ensure properly signed AAB/APK |
API_CHANGES_NOT_SENT_FOR_REVIEW | 400/403 | App rejected update, requires review acknowledgment | Add --changes-not-sent-for-review flag |
API_CHANGES_ALREADY_IN_REVIEW | 400 | Changes already in review | Use --error-if-in-review to prevent silent cancellation |
Network errors (exit code 5)
| Code | Message | Fix |
|---|---|---|
NETWORK_ERROR | Connection failed | Check internet and proxy settings |
NETWORK_TIMEOUT | Request timed out | Increase GPC_TIMEOUT |
NETWORK_DNS | DNS resolution failed | Check DNS; try 8.8.8.8 |
NETWORK_SSL | SSL/TLS error | Set GPC_CA_CERT for custom CA |
Configuration errors (exit code 1)
| Code | Message | Fix |
|---|---|---|
CONFIG_MISSING | No configuration found | Run gpc config init |
CONFIG_INVALID | Malformed .gpcrc.json | Fix JSON syntax |
CONFIG_APP_MISSING | No app specified | gpc config set app or --app flag |
Upload errors (exit code 4)
| Code | Message | Fix |
|---|---|---|
UPLOAD_CHUNK_FAILED | Chunk could not be sent after retries | Check network; increase GPC_MAX_RETRIES or GPC_UPLOAD_TIMEOUT |
UPLOAD_NO_COMPLETION | All bytes sent but no completion response | Retry upload; check GPC_UPLOAD_TIMEOUT |
UPLOAD_INITIATE_FAILED | Session initiation failed | Check auth and permissions; retry |
UPLOAD_NO_SESSION_URI | No Location header in initiation response | API error; retry or check service account permissions |
UPLOAD_SESSION_NOT_FOUND | Session expired (404) | Start a new upload session |
UPLOAD_SESSION_EXPIRED | Session gone (410) | Start a new upload session |
UPLOAD_INVALID_CHUNK_SIZE | Chunk size not multiple of 256 KB | Set GPC_UPLOAD_CHUNK_SIZE to a multiple of 262144 (256 KB) |
UPLOAD_INSECURE_URI | Session URI uses a non-HTTPS scheme | GPC rejects non-HTTPS upload URIs; check proxy or MITM stripping TLS |
UPLOAD_URI_HOST_MISMATCH | Session URI host does not match expected upload host | URI returned by Google API points to an unexpected host; do not proceed |
UPLOAD_INVALID_URI | Session URI is malformed or unparseable | API returned a bad Location header; retry the upload |
Release errors (exit code 4)
| Code | Message | Fix |
|---|---|---|
INVALID_BUNDLE | AAB is corrupted | Rebuild; run gpc validate first |
VERSION_CODE_CONFLICT | Version code exists | Increment versionCode |
RELEASE_NOT_FOUND | No release on track | Check with gpc releases list |
ROLLOUT_INVALID | Bad rollout percentage | Use 0-100, not decimal |
PROMOTE_NO_SOURCE | Source track empty | Upload to source track first |
EDIT_CONFLICT | Another edit is open | Only one edit at a time |
Monetization errors (exit code 4)
| Code | Message | Fix |
|---|---|---|
PRODUCT_NOT_FOUND | Invalid product ID | Verify with gpc subscriptions list or gpc iap list |
INVALID_PURCHASE_TOKEN | Token invalid or expired | Check token matches app/product |
PURCHASE_NOT_ACKNOWLEDGED | Purchase not ack'd in 3 days | Auto-refunded; acknowledge immediately next time |
SUBSCRIPTION_NOT_FOUND | Wrong subscription ID | Use gpc purchases subscription get |
User and tester errors (exit code 4)
| Code | Message | Fix |
|---|---|---|
DEVELOPER_ID_REQUIRED | Missing developer ID | Set GPC_DEVELOPER_ID or --developer-id |
USER_NOT_FOUND | Email not in account | Check with gpc users list |
INVALID_GRANT | Bad grant format | Use com.example.app:PERM1,PERM2 |
TESTER_LIMIT_EXCEEDED | Too many testers | Use Google Groups for scale |
TRACK_NOT_FOUND | Invalid track name | Use internal, alpha, beta, or custom |
Plugin errors (exit code 10)
| Code | Message | Fix |
|---|---|---|
PLUGIN_INVALID_PERMISSION | Unknown permission | Check valid permission list |
PLUGIN_NOT_APPROVED | Not in approvedPlugins | Add to approvedPlugins in .gpcrc.json |
Review-state errors (exit code 4)
API_CHANGES_NOT_SENT_FOR_REVIEW
HTTP: 400 or 403
Cause: The app has been flagged by Google Play and any update must explicitly acknowledge that changes are not sent for review. This commonly happens when a previous submission was rejected or when the app is in a policy compliance state that requires the flag.
Fix: Add the --changes-not-sent-for-review flag to your release or upload command.
# Upload with the required flag
gpc releases upload app.aab --track production --changes-not-sent-for-review
# Promote with the required flag
gpc releases promote --from beta --to production --changes-not-sent-for-reviewJSON error output:
{
"success": false,
"error": {
"code": "API_CHANGES_NOT_SENT_FOR_REVIEW",
"message": "This app requires the changesNotSentForReview flag to be set",
"suggestion": "Re-run the command with --changes-not-sent-for-review"
}
}Notes:
- This flag tells the API that you acknowledge the changes will not be sent for review automatically.
- Some apps enter this state after a policy violation or rejected review.
- The flag is safe to include on every call if your workflow requires it.
- Technical detail (v0.9.52+): When this flag is set, GPC skips the
edits.validateAPI call and goes straight toedits.commit. Google's validate endpoint does not accept thechangesNotSentForReviewparameter and returns "Unknown name" if you try. The commit endpoint handles validation internally. - Requires GPC v0.9.52+. Versions 0.9.51 and earlier had a bug where
edits.validateblocked this flag from ever reachingedits.commit.
---
API_CHANGES_ALREADY_IN_REVIEW
HTTP: 400
Cause: The track already has changes that are currently being reviewed by Google Play. Committing a new edit would silently cancel the in-progress review and replace it with the new submission, which may not be what you intended.
Fix: Use --error-if-in-review to make GPC fail early instead of silently replacing the in-review changes. If you do want to replace the in-review changes, omit the flag.
# Fail early if changes are already in review (recommended for CI)
gpc releases upload app.aab --track production --error-if-in-review
# If you intentionally want to replace the in-review release, omit the flag
gpc releases upload app.aab --track productionJSON error output:
{
"success": false,
"error": {
"code": "API_CHANGES_ALREADY_IN_REVIEW",
"message": "Track already has changes in review; committing would cancel the pending review",
"suggestion": "Use --error-if-in-review to prevent silent cancellation, or omit the flag to replace the in-review changes"
}
}Notes:
- In CI pipelines, always use
--error-if-in-reviewto avoid accidentally overwriting a release that is under review. - If you need to check the current review state before uploading, use
gpc releases list --track <track> --jsonto inspect the release status. - Without this flag, GPC will proceed and the previous in-review submission will be silently cancelled by the Google Play API.
---
Signing errors (exit code 4 or 6)
EDIT_CREATE_FAILED
Cause: Failed to create an edit session via the Play API. Usually a permissions issue with the service account.
Fix: Ensure the service account has "Release manager" or "Admin" role in Play Console (Setup > API access). Check that the package name is correct.
BUNDLES_LIST_FAILED
Cause: Failed to list bundles for the app within the edit session.
Fix: Verify the app has at least one uploaded AAB. Check service account permissions.
NO_BUNDLES
Cause: The bundles list returned empty. No AABs have been uploaded to this app.
Fix: Upload at least one AAB: gpc publish or gpc releases upload app.aab --track internal.
NO_SIGNING_CERT
Cause: The generatedApks endpoint returned no signing certificate fingerprint for the bundle version. This can happen if the service account lacks sufficient permissions or if Play App Signing is not enrolled.
Fix: Enroll in Play App Signing in Play Console. Ensure the service account has access to view generated APKs.
GENERATED_APKS_FAILED
Cause: HTTP error fetching generated APKs for a specific version code.
Fix: Check that the version code exists and the service account has permissions.
Signing key mismatch (exit code 6)
Cause: gpc preflight signing detected that the signing certificate changed between your two most recent bundle versions. This could indicate an unintended key rotation or a misconfigured upload.
Fix: If the change was intentional (key upgrade), this is safe to ignore. If not, investigate which build produced the mismatched bundle.
---
Environment variables for error recovery
| Variable | Default | Purpose |
|---|---|---|
GPC_TIMEOUT | 30000 | Request timeout in ms |
GPC_MAX_RETRIES | 5 | Max retry attempts |
GPC_BASE_DELAY | 1000 | Initial retry delay in ms |
GPC_MAX_DELAY | 15000 | Maximum retry delay in ms |
GPC_CA_CERT | — | Path to custom CA certificate |
HTTPS_PROXY | — | HTTP proxy URL |
GPC_DEBUG | — | Set to 1 for verbose output |
GPC_UPLOAD_TIMEOUT | 300000 | Upload request timeout in ms (5 min) |
GPC_UPLOAD_CHUNK_SIZE | 8388608 | Upload chunk size in bytes (8 MB) |
Exit Code Reference
Complete reference for all GPC exit codes and their meaning.
Exit codes
| Code | Category | Error Class | When |
|---|---|---|---|
| 0 | Success | — | Command completed successfully |
| 1 | Config | ConfigError | Invalid config, missing fields, bad .gpcrc.json |
| 2 | Usage | — | Invalid arguments, unknown flags, missing required args |
| 3 | Auth | AuthError | Bad credentials, expired token, no auth configured |
| 4 | API | PlayApiError | Google Play API returned an error (4xx, 5xx) |
| 5 | Network | NetworkError | Connection failed, timeout, DNS error, SSL error |
| 6 | Threshold | — | Vitals metric exceeded --threshold value |
| 10 | Plugin | — | Plugin permission validation failed |
Using exit codes in scripts
# Simple check
gpc vitals crashes --threshold 2.0
if [ $? -eq 6 ]; then
echo "Crash rate too high — blocking promotion"
exit 1
fi
# Case statement
gpc releases upload app.aab --track beta
case $? in
0) echo "Upload successful" ;;
3) echo "Auth failed — re-authenticate" ;;
4) echo "API error — check permissions" ;;
5) echo "Network error — check connectivity" ;;
*) echo "Unknown error" ;;
esacUsing exit codes in CI
GitHub Actions
- name: Check vitals
id: vitals
continue-on-error: true
run: gpc vitals crashes --threshold 1.5
- name: Block if threshold breached
if: steps.vitals.outcome == 'failure'
run: |
echo "Vitals check failed — blocking deployment"
exit 1GitLab CI
check-vitals:
script:
- gpc vitals crashes --threshold 1.5
- gpc vitals anr --threshold 0.4
allow_failure: false # Block pipeline on exit code != 0JSON error output
All errors include structured JSON when using --json:
{
"success": false,
"error": {
"code": "API_FORBIDDEN",
"message": "Service account lacks permission to manage production releases",
"suggestion": "Grant 'Release to production' permission in Play Console → Users & permissions"
}
}Fields:
code— machine-readable error identifiermessage— human-readable descriptionsuggestion— actionable fix (present on all GPC errors)
Exit code 6 — threshold breach
This is not an error — it's an intentional signal for CI gating.
# Returns exit 0 if crash rate ≤ 1.5%, exit 6 if > 1.5%
gpc vitals crashes --threshold 1.5
# JSON output includes threshold details
gpc vitals crashes --threshold 1.5 --json{
"success": false,
"thresholdBreached": true,
"value": 2.3,
"threshold": 1.5
}#!/usr/bin/env node
/**
* Detection script for GPC CLI.
* Returns JSON with installation status, version, auth state, and config.
* Used by Claude Code skill system for deterministic environment detection.
*
* Exit codes:
* 0 — GPC detected (may or may not be authenticated)
* 1 — GPC not found
*/
import { execSync } from "node:child_process";
import { existsSync } from "node:fs";
import { join } from "node:path";
function run(cmd) {
try {
return execSync(cmd, { encoding: "utf-8", timeout: 10000 }).trim();
} catch {
return null;
}
}
const result = {
installed: false,
version: null,
installMethod: null,
authStatus: null,
authMethod: null,
profile: null,
envAuth: false,
defaultApp: null,
configFile: null,
nodeVersion: process.version,
};
// Check if gpc is installed globally
const versionOutput = run("gpc --version");
if (!versionOutput) {
// Try npx
const npxVersion = run("npx gpc --version 2>/dev/null");
if (!npxVersion) {
console.log(JSON.stringify(result, null, 2));
process.exit(1);
}
result.version = npxVersion;
result.installed = true;
result.installMethod = "npx";
} else {
result.version = versionOutput;
result.installed = true;
result.installMethod = "global";
}
// Check auth status
const authOutput = run("gpc auth status --json 2>/dev/null");
if (authOutput) {
try {
const auth = JSON.parse(authOutput);
result.authStatus = auth.status || "unknown";
result.authMethod = auth.method || null;
result.profile = auth.profile || null;
} catch {
result.authStatus = "parse_error";
}
}
// Check for env-based auth
if (process.env.GPC_SERVICE_ACCOUNT) {
result.envAuth = true;
}
// Check default app
const configOutput = run("gpc config get app --json 2>/dev/null");
if (configOutput) {
try {
const config = JSON.parse(configOutput);
result.defaultApp = config.value || config.app || null;
} catch {
result.defaultApp = configOutput || null;
}
}
// Check for .gpcrc.json in current directory
const rcPath = join(process.cwd(), ".gpcrc.json");
if (existsSync(rcPath)) {
result.configFile = rcPath;
}
console.log(JSON.stringify(result, null, 2));
process.exit(0);