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

  1. 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.
  2. 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.
  3. 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

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


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/05a1a97aff9379da. Report an issue: GitHub.