Uploading a release candidate to Huawei AppGallery Connect, only to be met with an immediate rejection, is a strict bottleneck for release pipelines. When AppGallery Connect throws HarmonyOS Error Code 991, the deployment halts. The system is explicitly stating that the .app or .hap package contains a broken, invalid, or missing digital signature.
For Release Managers and DevOps engineers automating HarmonyOS app publishing, this error typically surfaces during automated CI/CD runs. It rarely occurs during local debugging. Understanding the strict packaging constraints of HarmonyOS NEXT is critical to resolving this deployment blocker permanently.
Anatomy of a HarmonyOS Signature Failure
Before implementing the fix, it is necessary to understand how HarmonyOS NEXT validates packages. A HarmonyOS application package (.app) is an archive containing one or more Harmony Ability Packages (.hap) and Harmony Shared Packages (.hsp).
When you submit this bundle to AppGallery Connect, the server validates the cryptographic integrity of the entire archive. HarmonyOS Error Code 991 triggers when the AppGallery Connect verification service detects a mismatch between the file hash and the signature block.
This occurs due to three primary root causes:
- Manual Repacking: A CI/CD pipeline unzips the
.appto inject environment variables, modify a configuration file, or alter assets, and then uses a standardziputility to repack it. This instantly invalidates the signature block. - Profile/Keystore Mismatch: The build pipeline attempts to sign a release binary using a
.p7b(Provisioning Profile) or.p12(Keystore) intended for development/debugging. - Improper Headless Execution: The DevEco Studio build tool (
hvigor) is invoked without the required signature parameters in the headless CI environment, resulting in an unsigned or partially signed App Gallery Connect artifact.
The Solution: Immutable Builds via Hvigor
The core philosophy to fix an AppGallery invalid package is immutability. You must never modify a .hap or .app file post-build. All environment specific injections must occur before the hvigor build tool generates and signs the package.
To resolve the DevEco Studio signature error in your pipeline, follow these specific steps to configure headless signing correctly.
Step 1: Configure the Build Profile
The build-profile.json5 file at the root of your HarmonyOS project dictates the signing configuration. Ensure that your release target points to the correct production credentials.
Do not hardcode sensitive passwords in this file. Instead, use environment variables that your CI/CD pipeline will provide.
// build-profile.json5
{
"app": {
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"storePassword": "$ENV_STORE_PASSWORD",
"keyAlias": "ReleaseKeyAlias",
"keyPassword": "$ENV_KEY_PASSWORD",
"certpath": "./sign/release_cert.cer",
"profile": "./sign/release_profile.p7b",
"storeFile": "./sign/release_keystore.p12"
}
}
],
"products": [
{
"name": "default",
"signingConfig": "default",
"compileSdkVersion": "5.0.0(12)",
"compatibleSdkVersion": "5.0.0(12)"
}
]
}
}
Step 2: Implement the CI/CD Build Script
In your DevOps environment (e.g., Jenkins, GitLab CI, GitHub Actions), you must execute the hvigorw command to build and sign simultaneously. Pass the secure passwords as command-line arguments to override the environment variables defined in the profile.
This bash script demonstrates the correct execution flow to produce a cleanly signed .app ready for AppGallery Connect.
#!/usr/bin/env bash
set -e
# Define strict paths for the HarmonyOS SDK and Node environment
export NODE_HOME="/usr/local/nodejs"
export PATH="$NODE_HOME/bin:$PATH"
export COMMANDLINE_TOOL_DIR="/opt/deveco-studio/tools/command-line-tools"
# Securely handle passwords from CI secrets (Do not echo these)
STORE_PASS=$AGCONNECT_STORE_PASSWORD
KEY_PASS=$AGCONNECT_KEY_PASSWORD
echo "Initiating HarmonyOS release build and sign process..."
# Execute hvigorw to clean and assemble the app package
# We pass the signing passwords directly into the hvigor task
./hvigorw clean assembleApp \
-p product=default \
-p buildMode=release \
-p storePassword="${STORE_PASS}" \
-p keyPassword="${KEY_PASS}" \
--no-daemon
echo "Build complete. Artifact generated at: ./build/outputs/default/app/"
Step 3: Validate the Signature Locally
Do not rely on AppGallery Connect as your signature validation tool. Uploading large .app files only to fail wastes pipeline minutes. Validate the signature directly within your CI script using the hapsigntool provided by the HarmonyOS SDK.
Add this validation step to your pipeline immediately after the hvigorw execution.
#!/usr/bin/env bash
APP_PACKAGE_PATH="./build/outputs/default/app/default.app"
HAP_SIGN_TOOL="/opt/deveco-studio/sdk/default/openharmony/toolchains/lib/hapsigntool.jar"
echo "Verifying signature block for $APP_PACKAGE_PATH"
# Run the validation command
java -jar "$HAP_SIGN_TOOL" verify-app \
-inFile "$APP_PACKAGE_PATH" \
-outCertChain ./build/outputs/cert_chain.cer \
-outProfile ./build/outputs/profile.p7b
# Check exit code
if [ $? -eq 0 ]; then
echo "Success: Package signature is valid."
else
echo "Error: Invalid package signature detected before upload."
exit 1
fi
Deep Dive: Why Manual Repacking Breaks the ASN.1 Structure
Understanding why the signature breaks helps prevent architectural mistakes in deployment pipelines. HarmonyOS uses a specific cryptographic signature block format based on PKCS #7.
When hvigor signs a package, it calculates the SHA-256 hash of every individual file inside the archive. These hashes are compiled into a manifest, which is then hashed again and encrypted using the private key from your .p12 keystore. This encrypted block is appended to the binary.
If a script unzips the .app, modifies a config.json or swaps an image, and re-zips the file, the underlying file bytes change. The new SHA-256 hash of the modified file will no longer match the hash recorded in the encrypted manifest. Furthermore, standard zip utilities strip the appended PKCS #7 signature block entirely. AppGallery Connect detects this missing or mismatched ASN.1 structure and immediately returns HarmonyOS Error Code 991.
Common Pitfalls and Edge Cases
Expired Provisioning Profiles
AppGallery Connect enforces strict expiration dates on .p7b provisioning profiles. If your automated pipeline is succeeding, but the upload fails with an invalid package error, verify the release_profile.p7b expiration date in the AppGallery Connect portal. Profiles must be renewed and updated in the repository regularly.
Misconfigured Build Flavors
DevOps teams frequently maintain multiple product flavors (e.g., staging and production). If your buildMode is set to release but the product flavor points to a signingConfig that references a debug .p12 file, the build will succeed. However, AppGallery Connect will reject it. Development certificates are strictly prohibited in the release upload API.
Corrupted Artifact Uploads
If the local hapsigntool validation passes but Error 991 persists on AppGallery Connect, the upload mechanism is the culprit. When utilizing the AppGallery Connect Publishing API via curl or custom Python scripts, ensure the binary data is not being mangled. Set the Content-Type to application/octet-stream and use the --data-binary flag in curl to preserve the exact byte structure of the signed package.