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

  1. Pass the exact ResPackage[] returned by ARSCDecoder.decode to ARSCDecoder.write — do not add, remove, or reorder packages.
  2. Ensure the input arsc stream in write() is the same file decoded earlier.
  3. If processing split APKs, run AndResGuard per APK rather than merging resources across them.
  4. 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

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


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)