Tencent/matrix · error · GradleScriptException

apksigner returned with status: $ret

Error message

apksigner returned with status: $ret

What it means

The script signs the patched temporary APK by invoking apksigner from the Android SDK build-tools and throws GradleScriptException with the exit code if apksigner exits non-zero, meaning signing failed.

Solutions

  1. Verify $ANDROID_HOME/build-tools/<buildToolsVersion>/apksigner exists and is executable
  2. Run the same apksigner command manually to see the real error output
  3. Ensure the keystore exists and its password matches the hardcoded pass:android or update the script
  4. Install/align the required build-tools version via sdkmanager

Example fix

// before
// buildToolsVersion from project, but not installed
// after
sdkmanager "build-tools;30.0.3" // match buildToolsVersion in script
Defensive patterns

Strategy: validation

Validate before calling

def apksigner = file("${sdkDir}/build-tools/${buildToolsVersion}/apksigner")
assert apksigner.exists() : "Install build-tools ${buildToolsVersion}"
assert keystoreFile.exists() : 'Keystore missing'

Prevention

When it happens

Trigger: apksigner exits non-zero due to a bad/incompatible keystore, wrong keystore password, missing build-tools version, corrupted patched APK, or apksigner not found at the expected sdkDir path (shell reports non-zero).

Common situations: Missing or mismatched build-tools installation; using the default 'android' debug keystore password with a custom keystore; JAVA/apksigner version incompatibilities (e.g. v1-only signatures vs minSdk); disk-full during tmp APK write.

Related errors


AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/7f4ca69f41e2c7dd. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/gradle/WeChatDebugStub2.gradle:203

                    if (Files.isDirectory(it)) {
                        Files.createDirectories(outPath)
                    } else {
                        Files.copy(it, outPath, StandardCopyOption.REPLACE_EXISTING)
                    }
                }
            } finally {
                outFS?.close()
                inFS?.close()
            }

            // Sign the resulting APK
            def proc = ["${sdkDir}/build-tools/${buildToolsVersion}/apksigner", 'sign', '--ks',
                        keystoreFile.absolutePath, '--ks-pass', 'pass:android', "${tmpFile}"]
                    .execute(null, file('.'))
            proc.consumeProcessOutput(System.out, System.err)
            def ret = proc.waitFor()
            if (ret != 0)
                throw new GradleScriptException("apksigner returned with status: $ret")

            // Move the resulting APK to replace the original output
            Files.move(tmpFile.toPath(), outputApk.toPath(), StandardCopyOption.REPLACE_EXISTING)
        } catch (e) {
            tmpFile.delete()
            throw e
        }
    }
}

View on GitHub (pinned to 3b8293bd65)