Tencent/tinker · error · ZipException
Invalid General Purpose Bit Flag: ${gpbf}
Error message
Invalid General Purpose Bit Flag: ${gpbf} What it means
While parsing a central directory entry, TinkerZipEntry reads the general purpose bit flag (GPBF) and rejects the archive if any bit in TinkerZipFile.GPBF_UNSUPPORTED_MASK is set. The mask equals GPBF_ENCRYPTED_FLAG (bit 0), so in practice this exception means the zip entry is encrypted and this reader — like Android's platform ZipFile — does not support encrypted archives.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipEntry.java:144
* at the CDE signature. If the GPBF_UTF8_FLAG is set in the CDE then
* UTF-8 is used to decode the string information, otherwise the
* defaultCharset is used.
*
* On exit, "in" will be positioned at the start of the next entry
* in the Central Directory.
*/
TinkerZipEntry(byte[] cdeHdrBuf, InputStream cdStream, Charset defaultCharset, boolean isZip64) throws IOException {
Streams.readFully(cdStream, cdeHdrBuf, 0, cdeHdrBuf.length);
BufferIterator it = HeapBufferIterator.iterator(cdeHdrBuf, 0, cdeHdrBuf.length,
ByteOrder.LITTLE_ENDIAN);
int sig = it.readInt();
if (sig != CENSIG) {
TinkerZipFile.throwZipException("unknown", cdStream.available(), "unknown", 0, "Central Directory Entry", sig);
}
it.seek(8);
int gpbf = it.readShort() & 0xffff;
if ((gpbf & TinkerZipFile.GPBF_UNSUPPORTED_MASK) != 0) {
throw new ZipException("Invalid General Purpose Bit Flag: " + gpbf);
}
// If the GPBF_UTF8_FLAG is set then the character encoding is UTF-8 whatever the default
// provided.
Charset charset = defaultCharset;
if ((gpbf & TinkerZipFile.GPBF_UTF8_FLAG) != 0) {
charset = Charset.forName("UTF-8");
}
compressionMethod = it.readShort() & 0xffff;
time = it.readShort() & 0xffff;
modDate = it.readShort() & 0xffff;
// These are 32-bit values in the file, but 64-bit fields in this object.
crc = ((long) it.readInt()) & 0xffffffffL;
compressedSize = ((long) it.readInt()) & 0xffffffffL;
size = ((long) it.readInt()) & 0xffffffffL;
int nameLength = it.readShort() & 0xffff;
int extraLength = it.readShort() & 0xffff;
int commentByteCount = it.readShort() & 0xffff;
// This is a 32-bit value in the file, but a 64-bit field in this object.View on GitHub (pinned to 1b7ea02c23)
Solutions
- Re-create the archive without encryption: `zip -Z store`/`zip -r plain.zip dir` (no -e/-P), or in Java write entries with java.util.zip which never encrypts.
- If you must consume encrypted zips, pre-decrypt them with a library that supports it (e.g. zip4j) into a plain stream/file before handing them to tinker-ziputils.
- Validate archives at ingest: check bit 0 of the GPBF in the central directory and reject early with a clear 'encrypted archives not supported' message.
Example fix
// before: feeding any downloaded archive straight to the reader
TinkerZipFile zf = new TinkerZipFile(downloadedFile); // throws Invalid GPBF on encrypted zip
// after: detect encryption up front and reject with a clear message
try (java.util.zip.ZipFile probe = new java.util.zip.ZipFile(downloadedFile)) {
// java.util.zip also refuses encrypted entries; failure here gives an early, clear signal
}
TinkerZipFile zf = new TinkerZipFile(downloadedFile); Defensive patterns
Strategy: validation
Validate before calling
// Detect the encrypted flag (bit 0 of GPBF) in the central directory before full parse
boolean isEncryptedZip(java.io.File f) throws java.io.IOException {
try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(f)) {
java.util.Enumeration<? extends java.util.zip.ZipEntry> es = zf.entries();
while (es.hasMoreElements()) {
// platform reader refuses encrypted entries; reaching here means it got past them
}
return false;
} catch (java.util.zip.ZipException e) {
return e.getMessage() != null && e.getMessage().toLowerCase().contains("encrypt");
}
} Try / catch
try {
TinkerZipFile zf = new TinkerZipFile(file);
} catch (java.util.zip.ZipException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Invalid General Purpose Bit Flag")) {
throw new IllegalArgumentException("encrypted or unsupported zip archives are not accepted: " + file, e);
}
throw e;
} Prevention
- Reject password-protected archives at ingest with a clear message.
- Standardize on unencrypted archives in build pipelines (no -e/-P flags).
- Pre-decrypt with a supporting library (e.g. zip4j) if encrypted input is unavoidable.
When it happens
Trigger: Opening an encrypted zip (password-protected, PKZIP/WinZip AES or traditional ZipCrypto) with TinkerZipFile or any code path that constructs TinkerZipEntry from a central directory stream; the CDE's flag field has bit 0 set and the exception is thrown during entry enumeration/read.
Common situations: Processing user-supplied or third-party archives that were password-protected by default (some Windows/macOS tools); CI fixtures generated with encryption; APKs passed through an 'app lock'/packer tool that re-zips with encryption; strong-encryption flags set by exotic archivers.
Related errors
- Invalid General Purpose Bit Flag: ${gpbf}
- Expected ${DEX_IN_JAR_NAME} in ${file}
- CRC mismatch
- Size mismatch
- Entry already exists: {}
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/05a1a97aff9379da.
Report an issue: GitHub.