shwenzhang/AndResGuard · error

String data size is not multiple of 4

Error message

String data size is not multiple of 4 (${size}).

What it means

StringBlock.read parses the string-pool chunk of resources.arsc (or an XML block). The size of the raw string data region is computed as (stylesOffset or chunkSize) minus stringsOffset, and the arsc format requires this region to be 4-byte aligned. If it isn't, the chunk offsets are inconsistent with the format and the block cannot be read, so an IOException is thrown.

Solutions

  1. Rebuild the APK with standard aapt/aapt2 so the string pool is correctly aligned, then retry
  2. Validate the input APK (aapt dump resources / badging) and use a clean, unmodified artifact
  3. Do not run other repacking/optimization tools on the APK before AndResGuard
  4. Update AndResGuard; check the exact size value in the message to identify the misaligned chunk

Example fix

// before: apk repacked by a zip-align-less tool
AndResGuard.process(repackedApk)
// after: rebuild and align
// zipalign -p -f 4 app-unsigned.apk app-aligned.apk
AndResGuard.process(alignedOriginalApk)
Defensive patterns

Strategy: validation

Validate before calling

// verify the string pool chunk is well-formed before parsing
int stringsOffset = header.stringsOffset;
int endOffset = (header.stylesOffset == 0) ? header.chunkSize : header.stylesOffset;
if ((endOffset - stringsOffset) % 4 != 0) {
  throw new IllegalArgumentException("Misaligned string pool in resources.arsc — rebuild the apk with aapt2");
}

Try / catch

try {
  StringBlock.read(reader);
} catch (IOException e) {
  if (e.getMessage().contains("not multiple of 4")) {
    throw new BuildException("String pool misaligned; the arsc was likely rewritten by a non-standard tool", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Reading a string pool where (stylesOffset == 0 ? chunkSize : stylesOffset) - stringsOffset is not a multiple of 4 — i.e. malformed offsets in the string pool header, a corrupted chunk, or reading the wrong bytes as a string pool.

Common situations: Corrupted resources.arsc or binary XML, APKs rewritten by tools that misalign string pools, or a decoder misreading a chunk boundary due to earlier parse errors.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of shwenzhang/AndResGuard@e4df245d82 (2026-09-12). Data as JSON: /api/errors/eec3c5b5316229f9. Report an issue: GitHub.

Appendix: source

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

    int styleCount = reader.readInt();
    int flags = reader.readInt();
    int stringsOffset = reader.readInt();
    int stylesOffset = reader.readInt();

    StringBlock block = new StringBlock();
    block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
    block.m_stringOffsets = reader.readIntArray(stringCount);
    block.m_stringOwns = new int[stringCount];
    Arrays.fill(block.m_stringOwns, -1);

    if (styleCount != 0) {
      block.m_styleOffsets = reader.readIntArray(styleCount);
    }
    {
      int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset;

      if ((size % 4) != 0) {
        throw new IOException("String data size is not multiple of 4 (" + size + ").");
      }
      block.m_strings = new byte[size];

      reader.readFully(block.m_strings);
    }
    if (stylesOffset != 0) {
      int size = (chunkSize - stylesOffset);
      if ((size % 4) != 0) {
        throw new IOException("Style data size is not multiple of 4 (" + size + ").");
      }
      block.m_styles = reader.readIntArray(size / 4);
    }
    return block;
  }

  public static int writeSpecNameStringBlock(
          ExtDataInput reader, ExtDataOutput out, Map<String, Set<String>> specNames, Map<String, Integer> curSpecNameToPos)
      throws IOException, AndrolibException {

View on GitHub (pinned to e4df245d82)