← Back to blog

Google Play version code already used: causes and fix

Every build you upload to Google Play carries a version code. It is one integer in your Gradle file, and it is the reason a lot of first uploads fail. Reuse a number and Play Console refuses the bundle. Pick a careless numbering scheme and you can run out of numbers. Neither problem is hard once you know the rules, but the rules are spread across several Google pages.

This guide collects them: what the "already used" error means, how the code differs from the version name, the maximum value, whether timestamp-based codes are safe, and what really happens when you need to roll back.

What does "version code already used" mean on Google Play?

It means the app bundle or APK you are uploading has a versionCode that an earlier upload for the same app already claimed. Google Play accepts each version code once per app. The fix is to raise versionCode in your Gradle file to a number higher than any you have uploaded, rebuild, and upload the new bundle.

The rule is in Google's Version your app guide: you cannot upload an APK to the Play Store with a version code you have already used for a previous version. Play Console Help adds conditions for updates. The version code must be greater than the current version, the package name must be the same, and the bundle must be signed with the same signature.

Google's documentation does not publish the exact error strings, so I will not quote one as official. The wording developers commonly report from the Play Console upload screen says the version code "has already been used" and tells you to try another. CI tools that upload through the Publishing API surface a similar message, which Codemagic lists in its troubleshooting docs. Whatever the wording, the cause and the fix are the same.

My original short post said Play blocks the upload silently. That was wrong: the upload fails with an error, not silently.

What is the difference between versionCode and versionName?

versionCode is a positive integer that Android and Google Play use internally to decide which build is newer, and users never see it. versionName is a string such as 1.4.0 that is shown to users and has no effect on update logic. You can ship ten builds named 1.4.0, but each one needs its own version code.

Both live in the module-level Gradle file. In Groovy:

// app/build.gradle
android {
    defaultConfig {
        applicationId "com.example.app"
        versionCode 7
        versionName "1.4.0"
    }
}

And in the Kotlin DSL:

// app/build.gradle.kts
android {
    defaultConfig {
        applicationId = "com.example.app"
        versionCode = 7
        versionName = "1.4.0"
    }
}

Android uses the version code on the device as well. The system protects against downgrades by refusing to install an APK with a lower version code than the one already installed. Google's guide also notes that values in the Gradle file override anything in the manifest, and recommends defining the version only in Gradle. The usual convention is to start at 1 and increase with every release, however small.

Where first-time publishers get caught

The error almost always comes from one of these:

  • Rebuilding without bumping. The first upload is version code 1. You fix a typo, rebuild, and the new bundle is still 1.
  • Uploading a bundle that is already in your library. If a build is in internal testing and you want the same build in production, do not upload it again. When you create the release, Play Console offers Add from library for previously uploaded versions. Same artifact, same code, no conflict.
  • Expecting a discarded draft to give the number back. Google's docs describe no way to free a version code once it has been used, so treat every uploaded number as spent.
  • Letting production get ahead of a test track. Play Console Help says users eligible for several tracks receive the highest version code published across them, and that an opted-in tester gets the production release if production has the higher code. Keep test builds numbered above production. This matters during the 12 testers closed testing requirement, when you may be shipping to several tracks at once.
  • Worrying about local builds. Version codes only matter at upload. Your debug and release variants can share a number on your own machine.

There is one documented exception. For internal app sharing, Play Console Help says version codes do not need to be new or unique, and you can reuse them for the builds you share that way.

What is the maximum version code Google Play allows?

The greatest versionCode Google Play allows is 2,100,000,000. Once your version code reaches that ceiling, you cannot upload new app bundles, which means no bug fixes and no feature updates for that app. Because every update needs a higher number than the last one, a scheme that starts high leaves you very little room.

That ceiling is slightly below the 32-bit signed integer maximum of 2,147,483,647, so a value that fits in an Int is not automatically acceptable. Play Console Help has a dedicated Version codes article on this. It recommends a generation strategy with gradual, consistent increases, regular monitoring of how fast the number is growing, and automated checks in your build that warn about excessively high values. If you do reach the limit, the article says Google Play may be able to provide support and tells you to contact the support team. That is not a position to plan for.

Is a timestamp-based version code safe?

A version code made from Unix time in seconds works today, but only until July 18, 2036, when the value passes 2,100,000,000. On October 2, 2026 it already produces about 1,790,900,000, roughly 85 percent of the allowed range. A CI build number, or minutes counted from a recent fixed date, is the safer choice.

This is the snippet from my original post:

// Groovy. Works, but read the caveats below.
defaultConfig {
    versionCode (System.currentTimeMillis() / 1000).toInteger()
}

It does what I said it does: an increasing integer with no manual tracking. What I left out was the arithmetic. 2,100,000,000 seconds after the Unix epoch is 13:20 UTC on July 18, 2036. An app that adopts this scheme has under ten years of uploads left, and it cannot back out by switching to small numbers later, because every update must carry a higher code than the current one. It is the excessively high value the Version codes article warns about.

Two schemes keep the convenience without the cliff. The first is a CI build number:

// app/build.gradle.kts
val ciBuild = providers.environmentVariable("BUILD_NUMBER")
    .orNull?.toIntOrNull()

android {
    defaultConfig {
        // Local builds fall back to 1 and are never uploaded.
        versionCode = ciBuild ?: 1
    }
}

The second keeps the timestamp idea but counts minutes from a recent fixed date:

// app/build.gradle.kts
val base = java.time.Instant.parse("2026-01-01T00:00:00Z")
val minutesSinceBase = java.time.Duration
    .between(base, java.time.Instant.now())
    .toMinutes().toInt()

android {
    defaultConfig {
        // About 394,560 on October 2, 2026.
        versionCode = minutesSinceBase
    }
}

At one unit per minute, that takes roughly 3,990 years to reach the limit. The catch is that two builds started in the same minute collide. Either scheme only works for an app whose published codes are still lower than the new values. If you have already shipped epoch-second codes, you cannot go down. Pick a fixed base just above your highest published code and add the CI build number to it. A base of 1,800,000,000, for example, leaves 300 million builds of headroom.

Can you roll back a release on Google Play?

Not by reusing an old version code, and not by downgrading users. You can halt a rollout so no more users get the release, and when you halt a fully rolled-out release the previous version becomes available again to new users. Users who already updated stay on that build until you publish a fix with a higher version code.

For a staged rollout, Play Console Help is explicit. Halting means no additional users receive the version, users who already received it remain on it, and the remedy for a bad bundle is to create and roll out a new release with a fixed one. I cover choosing the percentages in staged rollout percentages on Google Play.

Halting a release that has already reached 100% is a newer option. According to the Play Console Help page for it, a previously live, fully rolled-out version automatically takes the halted release's place for new and eligible users. There are limits: you cannot halt the first release on a track, and a previous release with a policy violation cannot be used as the replacement. You halt from the Releases tab under Manage rollout, and you can resume later to any percentage.

Neither option moves an existing user backwards, because of the downgrade protection described earlier. So a real rollback is a new build of your last good code with a new, higher version code. Keep the previous release buildable for that reason. The Android vitals bad behavior thresholds are a sensible trigger for deciding when to halt, and it helps to know how long Google Play app review takes before you promise anyone a fix time.

Where IOn Emit fits

The version code is set in your build, so every fix above lives in Gradle. The step after the build is where IOn Emit comes in. It is a Windows desktop app for publishing to Google Play. Its quick push flow lets you pick the app, drop in the bundle, and select the track and rollout percentage, with all four release tracks and staged rollout control, plus pre-publish validation. The publishing suite is free.

Version code checklist

  1. versionCode is defined in Gradle only, not in the manifest as well.
  2. Each uploaded build has a code higher than every code uploaded before for that app.
  3. To put an existing build on another track, you use Add from library instead of uploading it again.
  4. Builds on test tracks are numbered above the current production build.
  5. Your numbering scheme leaves plenty of room below 2,100,000,000.
  6. If you use epoch seconds, you have a plan to move to a base plus a counter before 2036.
  7. The build warns or fails when the version code is unexpectedly high.
  8. You can rebuild the last good release with a new, higher code if you need to roll back.

This post is part of our app publishing guides for indie Android developers.

Try it free
IOn Emit - Free to start

Publish to Google Play from a Windows desktop app: a 9-step pre-flight wizard, pre-publish validation, AI descriptions, a screenshot studio, and a 100-point ASO score. The publishing suite is free. Pro is $19/mo.

→
Keep reading
IOn Emit Android Vitals Bad Behavior Thresholds, Explained (2026) Read → IOn Emit How Long Does Google Play App Review Take? Read → IOn Emit The Google Play 12 Testers Requirement, Explained Read →
Reading us on Google?
Add The IOn Project as a preferred source

One click on Google’s preferences page, and our articles show up more often in your Top Stories, AI Overviews, and AI Mode.

→