shwenzhang/AndResGuard · error · brut.androlib.AndrolibException
writeTable package count is different before
Error message
writeTable package count is different before %d, now %d
What it means
writeTable reads the package count from the original resources.arsc and validates it against the number of ResPackage objects produced during decode (mPkgs.length). If they differ, the in-memory package set no longer matches the arsc being rewritten — an internal consistency invariant — so AndResGuard aborts rather than writing a corrupt resource table.
Solutions
- Pass the exact ResPackage[] returned by ARSCDecoder.decode to ARSCDecoder.write — do not add, remove, or reorder packages.
- Ensure the input arsc stream in write() is the same file decoded earlier.
- If processing split APKs, run AndResGuard per APK rather than merging resources across them.
- Update AndResGuard — some arsc variants (shared libraries, package id offsets) were fixed in later versions.
Example fix
// before ResPackage[] pkgs = ARSCDecoder.decode(in, decoder); ResPackage[] filtered = keepOnly(pkgs); // count no longer matches arsc ARSCDecoder.write(in, decoder, filtered); // after ResPackage[] pkgs = ARSCDecoder.decode(in, decoder); ARSCDecoder.write(in, decoder, pkgs); // unchanged array
Defensive patterns
Strategy: validation
Validate before calling
if (pkgs.length != expectedPackageCountFromArsc) {
throw new IllegalStateException("package array must be passed unchanged from decode");
} Try / catch
try {
ARSCDecoder.write(in, decoder, pkgs);
} catch (AndrolibException e) {
if (e.getMessage().contains("package count is different")) {
// you filtered/merged packages; rerun decode->write unchanged
}
} Prevention
- Always pass the exact ResPackage[] from decode to write.
- Process split APKs individually; never merge resources across them.
- Use one arsc stream for both decode and write.
When it happens
Trigger: Calling ARSCDecoder.write with pkgs whose length differs from the packageCount stored in the input arsc — e.g. packages added/removed after decode, decode of one arsc combined with write of another, or shared-library/multi-package APKs decoded into fewer packages than declared.
Common situations: Custom pipelines filtering or merging ResPackage arrays between decode and write; processing APKs with package counts AndResGuard mishandles (some split/dynamic-feature APKs); mixing streams from two different APKs.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- writeEntry new specNamesId < 0
- Could not decode arsc file
- Neither KeyStore key alias nor private key file available
- Failed to determine APK's minimum supported platform…
- KeyStore (--ks) must be specified
AI-assisted analysis of shwenzhang/AndResGuard@e4df245d82 (2026-09-12).
Data as JSON: /api/errors/15b533354ed28815.
Report an issue: GitHub.
Appendix: source
Thrown at AndResGuard-core/src/main/java/com/tencent/mm/androlib/res/decoder/ARSCDecoder.java:225
mMappingWriter.close();
System.out.printf("resources mapping file %s done\n", mApkDecoder.getResMappingFile().getAbsolutePath());
generalFilterEnd(mMergeDuplicatedResCount, mMergeDuplicatedResTotalSize);
mMergeDuplicatedResMappingWriter.close();
System.out.printf("resources filter mapping file %s done\n", mApkDecoder.getMergeDuplicatedResMappingFile().getAbsolutePath());
return packages;
}
private void writeTable() throws IOException, AndrolibException {
System.out.printf("writing new resources.arsc \n");
mTableLenghtChange = 0;
writeNextChunkCheck(Header.TYPE_TABLE, 0);
int packageCount = mIn.readInt();
mOut.writeInt(packageCount);
mTableLenghtChange += StringBlock.writeTableNameStringBlock(mIn, mOut, mTableStringsResguard);
writeNextChunk(0);
if (packageCount != mPkgs.length) {
throw new AndrolibException(String.format("writeTable package count is different before %d, now %d",
mPkgs.length,
packageCount
));
}
for (int i = 0; i < packageCount; i++) {
mCurPackageID = i;
writePackage();
}
// 最后需要把整个的size重写回去
reWriteTable();
}
private void generalFileResMapping() throws IOException {
mMappingWriter.write("res path mapping:\n");
for (String raw : mOldFileName.keySet()) {
mMappingWriter.write(" " + raw + " -> " + mOldFileName.get(raw));
mMappingWriter.write("\n");
}View on GitHub (pinned to e4df245d82)