Keystore file not found for signing config 'externalOverride'
When running Rock Remote Build - Android with sign: true, the build fails because the Android Gradle Plugin (AGP) cannot find the keystore file. It looks for it in the Gradle daemon directory instead of where the action actually places it.
Error from logs
> Keystore file '/home/runner/.gradle/daemon/9.0.0/release.keystore' not found for signing config 'externalOverride'.
The exact daemon path may vary (e.g. 9.0.0 depends on the Gradle version). The filename (release.keystore) comes from the keystore-store-file input.
Steps to reproduce
-
Use the Rock Remote Build - Android action with signing enabled:
sign: true
keystore-base64 (or keystore-file) and keystore-store-file (e.g. release.keystore) set
keystore-store-password, keystore-key-alias, keystore-key-password set
keystore-path not set explicitly (default release.keystore is used)
-
Run the workflow.
-
The "Build Android" step fails with the error above.
Expected behavior
The build should succeed and produce a signed APK/AAB. The keystore is decoded and written by the "Decode and store keystore" step to a path like android/app/release.keystore (or $ANDROID_SOURCE_DIR/$APP_NAME/release.keystore). AGP should use that file for the externalOverride signing config.
Actual behavior
AGP resolves android.injected.signing.store.file as a path. When it receives a relative value (e.g. release.keystore), it resolves it from the Gradle daemon working directory (e.g. /home/runner/.gradle/daemon/9.0.0/), so it tries to open:
/home/runner/.gradle/daemon/9.0.0/release.keystore
That file does not exist. The real keystore is at e.g.:
$GITHUB_WORKSPACE/android/app/release.keystore (or similar, depending on keystore-path and project layout).
Root cause
-
The action writes android.injected.signing.* properties to $HOME/.gradle/gradle.properties. AGP reads these and creates an externalOverride signing config.
-
For android.injected.signing.store.file, the action was writing the bare filename from the keystore-store-file input (e.g. release.keystore), not the full path where the keystore is stored.
-
AGP treats android.injected.signing.store.file as a path. For relative values, it resolves them from the Gradle daemon directory, not from the project or android/app/.
-
Relative path + wrong base directory → "Keystore file not found" for externalOverride.
The action also sets ROCK_UPLOAD_STORE_FILE and similar; apps using file(ROCK_UPLOAD_STORE_FILE) in build.gradle can resolve correctly relative to the project. However, android.injected.signing.* takes precedence, so AGP uses externalOverride with the wrong path.
So commenting out the android.injected.signing.* parts also fixes the problem for me.
Suggested fix
-
Run "Create local gradle.properties" after "Decode and store keystore" so the absolute path (KEYSTORE_TARGET_PATH) is available when writing gradle.properties.
-
Set android.injected.signing.store.file to that absolute path instead of the filename:
android.injected.signing.store.file=$KEYSTORE_TARGET_PATH
KEYSTORE_TARGET_PATH is the absolute path produced by the "Decode and store keystore" step (e.g. /home/runner/work/.../android/app/release.keystore).
-
Leave android.injected.signing.store.password, key.alias, key.password, and all ROCK_UPLOAD_* properties unchanged.
With an absolute path, AGP’s externalOverride finds the keystore correctly. No changes are required in build.gradle or app code.
Environment
- Action: Rock Remote Build - Android (composite action)
- Trigger:
sign: true with valid keystore inputs
- Runner: GitHub-hosted (e.g.
ubuntu-latest); the daemon path is under /home/runner/.gradle/daemon/
- AGP: reads
android.injected.signing.* from ~/.gradle/gradle.properties and creates externalOverride signing config
Keystore file not found for signing config 'externalOverride'
When running Rock Remote Build - Android with
sign: true, the build fails because the Android Gradle Plugin (AGP) cannot find the keystore file. It looks for it in the Gradle daemon directory instead of where the action actually places it.Error from logs
The exact daemon path may vary (e.g.
9.0.0depends on the Gradle version). The filename (release.keystore) comes from thekeystore-store-fileinput.Steps to reproduce
Use the Rock Remote Build - Android action with signing enabled:
sign: truekeystore-base64(orkeystore-file) andkeystore-store-file(e.g.release.keystore) setkeystore-store-password,keystore-key-alias,keystore-key-passwordsetkeystore-pathnot set explicitly (defaultrelease.keystoreis used)Run the workflow.
The "Build Android" step fails with the error above.
Expected behavior
The build should succeed and produce a signed APK/AAB. The keystore is decoded and written by the "Decode and store keystore" step to a path like
android/app/release.keystore(or$ANDROID_SOURCE_DIR/$APP_NAME/release.keystore). AGP should use that file for theexternalOverridesigning config.Actual behavior
AGP resolves
android.injected.signing.store.fileas a path. When it receives a relative value (e.g.release.keystore), it resolves it from the Gradle daemon working directory (e.g./home/runner/.gradle/daemon/9.0.0/), so it tries to open:/home/runner/.gradle/daemon/9.0.0/release.keystoreThat file does not exist. The real keystore is at e.g.:
$GITHUB_WORKSPACE/android/app/release.keystore(or similar, depending onkeystore-pathand project layout).Root cause
The action writes
android.injected.signing.*properties to$HOME/.gradle/gradle.properties. AGP reads these and creates anexternalOverridesigning config.For
android.injected.signing.store.file, the action was writing the bare filename from thekeystore-store-fileinput (e.g.release.keystore), not the full path where the keystore is stored.AGP treats
android.injected.signing.store.fileas a path. For relative values, it resolves them from the Gradle daemon directory, not from the project orandroid/app/.Relative path + wrong base directory → "Keystore file not found" for
externalOverride.The action also sets
ROCK_UPLOAD_STORE_FILEand similar; apps usingfile(ROCK_UPLOAD_STORE_FILE)inbuild.gradlecan resolve correctly relative to the project. However,android.injected.signing.*takes precedence, so AGP usesexternalOverridewith the wrong path.So commenting out the
android.injected.signing.*parts also fixes the problem for me.Suggested fix
Run "Create local gradle.properties" after "Decode and store keystore" so the absolute path (
KEYSTORE_TARGET_PATH) is available when writinggradle.properties.Set
android.injected.signing.store.fileto that absolute path instead of the filename:KEYSTORE_TARGET_PATHis the absolute path produced by the "Decode and store keystore" step (e.g./home/runner/work/.../android/app/release.keystore).Leave
android.injected.signing.store.password,key.alias,key.password, and allROCK_UPLOAD_*properties unchanged.With an absolute path, AGP’s
externalOverridefinds the keystore correctly. No changes are required inbuild.gradleor app code.Environment
sign: truewith valid keystore inputsubuntu-latest); the daemon path is under/home/runner/.gradle/daemon/android.injected.signing.*from~/.gradle/gradle.propertiesand createsexternalOverridesigning config