shwenzhang/AndResGuard · error · brut.androlib.AndrolibException

readEntry replaceString == null

Error message

readEntry replaceString == null

What it means

While decoding a resource entry for obfuscation, readEntry obtains a replacement short name from the ResguardBuilder. If the replace string comes back null (no obfuscated name available for the current entry ID) AndResGuard throws, because every resguard-able entry must get a name for the arsc mapping.

Solutions

  1. Upgrade AndResGuard (and AGP/AndResGuard Gradle plugin) to the latest version — arsc compatibility fixes land frequently.
  2. Rebuild the APK with standard AAPT2 and retry without other resource-processing tools applied first.
  3. Check whether the APK's arsc uses unusual features (sparse entries, shared libraries); build with aaptOptions/noCompress defaults or flatten those features.
  4. Report the failing APK's arsc structure to the AndResGuard maintainers if it still reproduces.

Example fix

// before (gradle)
apply plugin: 'AndResGuard' // old 1.2.x with AGP 4.x APKs
// after
classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.21' // supports modern aapt2 output
Defensive patterns

Strategy: try-catch

Try / catch

try {
    ResPackage[] pkgs = ARSCDecoder.decode(arscStream, apkDecoder);
} catch (AndrolibException e) {
    if (e.getMessage().contains("replaceString == null")) {
        // arsc format unsupported by this AndResGuard version: upgrade plugin
    }
}

Prevention

When it happens

Trigger: Decoding a resources.arsc whose entry ordering/IDs don't match what the ResguardBuilder precomputed — e.g. arsc format variants AAPT2 emits that the builder didn't account for, or entry/ID lists desynchronized so getReplaceString(mCurEntryID) has no entry at the current index.

Common situations: APKs built with newer AGP/AAPT2 versions than the AndResGuard release supports; arsc with unusual entry layouts (sparse entries, type/entry id offsets); protected/packed APKs with tampered arsc.

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/dd48697622bd2c40. Report an issue: GitHub.

Appendix: source

Thrown at AndResGuard-core/src/main/java/com/tencent/mm/androlib/res/decoder/ARSCDecoder.java:648

        String typeName = mType.getName();
        if (typeMaps.containsKey(typeName)) {
          HashMap<String, String> nameMap = typeMaps.get(typeName);
          String specName = mSpecNames.get(specNamesId).toString();
          if (nameMap.containsKey(specName)) {
            keepMapping = true;
            replaceString = nameMap.get(specName);
          }
        }
      }
    }

    if (!keepMapping) {
      replaceString = mResguardBuilder.getReplaceString();
    }

    mResguardBuilder.setInReplaceList(mCurEntryID);
    if (replaceString == null) {
      throw new AndrolibException("readEntry replaceString == null");
    }
    generalResIDMapping(mPkg.getName(), mType.getName(), mSpecNames.get(specNamesId).toString(), replaceString);
    mPkg.putSpecNamesReplace(mResId, replaceString);
    // arsc name列混淆成固定名字, 减少string pool大小
    boolean useFixedName = config.mFixedResName != null && config.mFixedResName.length() > 0;
    String fixedName = useFixedName ? config.mFixedResName : replaceString;
    mPkg.putSpecNamesblock(fixedName, replaceString);
    mType.putSpecResguardName(replaceString);
  }

  private void writeEntry() throws IOException, AndrolibException {
    /* size */
    mOut.writeBytes(mIn, 2);
    short flags = mIn.readShort();
    mOut.writeShort(flags);
    int specNamesId = mIn.readInt();
    ResPackage pkg = mPkgs[mCurPackageID];
    if (pkg.isCanResguard()) {

View on GitHub (pinned to e4df245d82)