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
- 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.
- 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.
- In CI, verify the keystore placement step (base64 decode / download) actually produced the file before gradle runs.
- 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
- Use absolute keystore paths in CI signing properties; use ../../ relative paths only if the file lives inside the repo.
- Remember the resolution base is the directory of the .properties file (platforms/android/), not the repo root or cwd.
- Make the CI step that decodes/downloads the keystore a hard dependency of the build step, and assert the file exists in between.
- Generate signing properties with one consistent cordova build --release --keystore=... invocation instead of editing files across machines.
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
- Failed to install apk to target: ${output}
- ${filePath}: Missing key required "${key}"
- Expected Android Build Tools version >= ${cordovaConfig.MIN_
- Specified build config file does not exist: ${buildConfig}
- Malformed BoM platform: ${p}
AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22).
Data as JSON: /api/errors/19fc9bf5e980e076.
Report an issue: GitHub.