apache/cordova-android · error · FileNotFoundException

Keystore file does not exist

Error message

Keystore file does not exist: ${storeFile.getAbsolutePath()}

What it means

addSigningProps runs for the debug and release signing configs whenever a signing properties file is in play — platforms/android/debug-signing.properties / release-signing.properties (the template falls back to ../release-signing.properties from the app module) or a path passed via the cdvReleaseSigningPropertiesFile / cdvDebugSigningPropertiesFile gradle properties (set by the cordova CLI when you build with --keystore flags). It reads key.store (or storeFile) and, crucially, resolves RELATIVE paths against the properties file's parent directory (platforms/android/), not the repo root or the shell's cwd. If the resolved file does not exist, it throws FileNotFoundException showing the exact absolute path it checked.

Solutions

  1. Look at the absolute path in the message — that is exactly where gradle looked. Either place the keystore there or fix the path in the properties file to point at the real file.
  2. Remember the resolution base: relative key.store / storeFile values resolve against the directory containing the .properties file (platforms/android/), so a repo-root keystore needs ../../myapp.keystore — or better, an absolute path.
  3. In CI, verify the keystore placement step (base64 decode / download) actually produced the file before gradle runs.
  4. Regenerate the signing configuration in one consistent shot: cordova build android --release --keystore=/abs/path/to/app.keystore --alias=... so the CLI rewrites the properties file with the value you pass.

Example fix

# before — platforms/android/release-signing.properties
key.store=my-release-key.keystore
# gradle looks at platforms/android/my-release-key.keystore (missing — file is at repo root)

# after
key.store=../../my-release-key.keystore
# or absolute:
key.store=/home/me/keys/my-release-key.keystore
Defensive patterns

Strategy: validation

Validate before calling

# Resolve the keystore exactly like addSigningProps does (relative to the .properties file) and check it exists
PROPS=platforms/android/release-signing.properties
STORE=$(sed -n 's/^key\.store=//p' "$PROPS" | head -1)
[ -z "$STORE" ] && STORE=$(sed -n 's/^storeFile=//p' "$PROPS" | head -1)
case "$STORE" in
  /*) F="$STORE" ;;
  *)  F="$(cd "$(dirname "$PROPS")"/"$(dirname "$STORE")" && pwd)/$(basename "$STORE")" ;;
esac
[ -f "$F" ] || { echo "Keystore missing at $F — fix the path in $PROPS or place the file"; exit 1; }

Prevention

When it happens

Trigger: A relative keystore path that assumes the repo root but gets resolved against platforms/android/; the keystore was moved, renamed, or deleted after the signing properties were generated; CI checkout that never placed the keystore (secret/base64 decode step failed or ran after gradle); an absolute path from another machine (e.g. a macOS path on a Linux runner).

Common situations: cordova build android --release --keystore=../myapp.keystore run later from a different working directory; keystores kept outside the repo so every machine needs its own path; CI secrets step failing silently before the build stage; team members sharing release-signing.properties with machine-specific paths.

Related errors


AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22). Data as JSON: /api/errors/19fc9bf5e980e076. Report an issue: GitHub.

Appendix: source

Thrown at templates/project/app/build.gradle:344

    // SUB-PROJECT DEPENDENCIES START
    debugCompile(project(path: ":CordovaLib", configuration: "debug"))
    releaseCompile(project(path: ":CordovaLib", configuration: "release"))
    // SUB-PROJECT DEPENDENCIES END
}

def addSigningProps(propsFilePath, signingConfig) {
    def propsFile = file(propsFilePath)
    def props = new Properties()
    propsFile.withReader { reader ->
        props.load(reader)
    }

    def storeFile = new File(props.get('key.store') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
    if (!storeFile.isAbsolute()) {
        storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
    }
    if (!storeFile.exists()) {
        throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
    }
    signingConfig.keyAlias = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
    signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword))
    signingConfig.storeFile = storeFile
    signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword))
    def storeType = props.get('storeType', props.get('key.store.type', ''))
    if (!storeType) {
        def filename = storeFile.getName().toLowerCase()
        if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
            storeType = 'pkcs12'
        } else {
            storeType = signingConfig.storeType // "jks"
        }
    }
    signingConfig.storeType = storeType
}

for (def func : cdvPluginPostBuildExtras) {

View on GitHub (pinned to 7c1e190064)