apache/cordova-android · error · FileNotFoundException

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

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.

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)

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.