Tencent/tinker · error · ZipException
file name:${filename}, file size${fileSize}, entry name:${en
Error message
file name:${filename}, file size${fileSize}, entry name:${entryName}, entry localHeaderRelOffset:${localHeaderRelOffset}, ${msg} signature not found; was ${hexString} What it means
throwZipException is the generic guard for magic-number mismatches: when the library seeks to an entry's local file header and the first 4 bytes are not the 'PK\x03\x04' signature, it throws a ZipException that embeds the file name, file size, entry name, the entry's localHeaderRelOffset, the kind of header that failed, and the actual (wrong) signature in hex. The context fields let you tell whether the offset itself is wrong or the bytes at the offset are garbage.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java:233
// If we don't have a zip64 eocd record, we read values from the "regular"
// eocd record.
int diskNumber = it.readShort() & 0xffff;
int diskWithCentralDir = it.readShort() & 0xffff;
numEntries = it.readShort() & 0xffff;
int totalNumEntries = it.readShort() & 0xffff;
it.skip(4); // Ignore centralDirSize.
centralDirOffset = ((long) it.readInt()) & 0xffffffffL;
if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
throw new ZipException("Spanned archives not supported");
}
}
final int commentLength = it.readShort() & 0xffff;
return new EocdRecord(numEntries, centralDirOffset, commentLength);
}
static void throwZipException(String filename, long fileSize, String entryName, long localHeaderRelOffset, String msg, int magic) throws ZipException {
final String hexString = Integer.toHexString(magic);
throw new ZipException("file name:" + filename
+ ", file size" + fileSize
+ ", entry name:" + entryName
+ ", entry localHeaderRelOffset:" + localHeaderRelOffset
+ ", "
+ msg + " signature not found; was " + hexString);
}
/**
* Closes this zip file. This method is idempotent. This method may cause I/O if the
* zip file needs to be deleted.
*
* @throws IOException
* if an IOException occurs.
*/
public void close() throws IOException {
// guard.close();
RandomAccessFile localRaf = raf;
if (localRaf != null) { // Only close initialized instancesView on GitHub (pinned to 1b7ea02c23)
Solutions
- Inspect the reported localHeaderRelOffset and hex signature: a plausible offset with garbage bytes means the file body changed after indexing — reopen the TinkerZipFile so the central directory is re-read from the current file.
- If the archive was modified (patched, repackaged, SFX-wrapped), rebuild it cleanly so central-directory offsets match the file contents.
- Re-download or restore the file from a known-good source; verify with an independent tool (unzip -t) before retrying.
Example fix
// before
TinkerZipFile zf = new TinkerZipFile(file); // opened before file was patched
InputStream is = zf.getInputStream(entry); // stale offsets
// after
zf.close();
file = rebuildOrRedownload(file);
try (TinkerZipFile zf2 = new TinkerZipFile(file)) {
InputStream is = zf2.getInputStream(zf2.getEntry(entry.getName()));
} Defensive patterns
Strategy: try-catch
Validate before calling
// hard to pre-validate cheaply; instead never mutate a file while it is open
// reopen after any external modification:
if (file.lastModified() > openTimestamp) {
zf.close();
zf = new TinkerZipFile(file);
} Try / catch
try {
InputStream is = zf.getInputStream(entry);
} catch (ZipException e) {
if (e.getMessage().contains("signature not found")) {
// archive changed under us or is corrupt: reopen once from a verified copy, else fail
log.error("Local header mismatch for {} at {}: {}", entry.getName(), file, e.getMessage());
throw new IOException("Archive inconsistent, refusing to read: " + file, e);
}
throw e;
} Prevention
- Treat zip files as immutable while a TinkerZipFile is open; reopen after external rewrites.
- Checksum archives before processing so corruption is detected before offsets are used.
- Never modify an APK/zip in place (patch, resign, SFX-wrap) while readers hold it open.
When it happens
Trigger: TinkerZipFile.getInputStream(entry) on an entry whose local header offset points at non-header bytes — e.g. the archive was rewritten/concatenated after the central directory was parsed, the zip has a prepended payload (self-extracting exe) with stale offsets, or the file is truncated/corrupted.
Common situations: APK/patch files modified after the central directory was indexed (e.g. binary-diff tools rewriting in place); self-extracting archives where central-dir offsets need adjustment; partial downloads where the tail is intact but the body is not; zips with data descriptors confusing offset math.
Related errors
- Local file header offset is after central directory
- Not a zip archive
- Duplicate entry name: ${entryName}
- zipEntry is null when get from oldApk
- Expected ${DEX_IN_JAR_NAME} in ${file}
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/f054d1d656d7ef8b.
Report an issue: GitHub.