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
- Rebuild the APK with standard aapt/aapt2 so the string pool is correctly aligned, then retry
- Validate the input APK (aapt dump resources / badging) and use a clean, unmodified artifact
- Do not run other repacking/optimization tools on the APK before AndResGuard
- 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
- Run zipalign (4-byte) on APKs before/after any repacking
- Avoid third-party tools that rewrite arsc string pools
- Validate with aapt dump resources before obfuscation
- Rebuild from source rather than patching binary arsc files
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
- Config size < 28
- Invalid chunk type: expected=0x%08x, got=0x%08x
- Invalid chunk type: expected=
- readAndWriteHeader size < 0: size=
- Could not decode arsc file
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)