Tencent/tinker · error · IllegalArgumentException

Bad mode: ${mode}

Error message

Bad mode: ${mode}

What it means

TinkerZipFile's constructor only permits mode == OPEN_READ or mode == OPEN_READ|OPEN_DELETE; anything else throws IllegalArgumentException('Bad mode') before the file is opened. OPEN_DELETE makes the file delete-on-close. Passing plain 0, OPEN_DELETE alone, or invented flag values is rejected because the class only supports reading.

Source

Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java:134

    public TinkerZipFile(String name) throws IOException {
        this(new File(name), OPEN_READ);
    }
    /**
     * Constructs a new {@code ZipFile} allowing access to the given file.
     *
     * <p>UTF-8 is used to decode all comments and entry names in the file.
     *
     * <p>The {@code mode} must be either {@code OPEN_READ} or {@code OPEN_READ|OPEN_DELETE}.
     * If the {@code OPEN_DELETE} flag is supplied, the file will be deleted at or before the
     * time that the {@code ZipFile} is closed (the contents will remain accessible until
     * this {@code ZipFile} is closed); it also calls {@code File.deleteOnExit}.
     *
     * @throws IOException if an {@code IOException} occurs.
     */
    public TinkerZipFile(File file, int mode) throws IOException {
        filename = file.getPath();
        if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE)) {
            throw new IllegalArgumentException("Bad mode: " + mode);
        }
        if ((mode & OPEN_DELETE) != 0) {
            fileToDeleteOnClose = file;
            fileToDeleteOnClose.deleteOnExit();
        } else {
            fileToDeleteOnClose = null;
        }
        raf = new RandomAccessFile(filename, "r");

        readCentralDir();
        // guard.open("close");
    }

    /**
     * Returns true if the string is null or 0-length.
     * @param str the string to be examined
     * @return true if str is null or zero length
     */

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Use exactly TinkerZipFile.OPEN_READ, or OPEN_READ|TinkerZipFile.OPEN_DELETE if you want delete-on-close.
  2. Reference the constants from TinkerZipFile itself, not from java.util.zip.ZipFile or literal ints.
  3. If you constructed a combined mode variable, assert (mode & ~OPEN_DELETE) == OPEN_READ before calling the constructor.

Example fix

// before
new TinkerZipFile(new File(path), ZipFile.OPEN_READ | someFlag);

// after
new TinkerZipFile(new File(path), TinkerZipFile.OPEN_READ);
Defensive patterns

Strategy: validation

Validate before calling

int mode = TinkerZipFile.OPEN_READ;
if (deleteOnClose) {
    mode |= TinkerZipFile.OPEN_DELETE;
}
assert (mode & ~TinkerZipFile.OPEN_DELETE) == TinkerZipFile.OPEN_READ;
TinkerZipFile zf = new TinkerZipFile(file, mode);

Prevention

When it happens

Trigger: new TinkerZipFile(file, mode) with mode outside {OPEN_READ, OPEN_READ|OPEN_DELETE} — e.g. mode = 0, mode = OPEN_DELETE without OPEN_READ, or OR-ing in a write flag from java.util.zip.ZipFile (this class has no write mode).

Common situations: Copying constructor calls from code that targeted java.util.zip.ZipFile with different constants; building the mode dynamically from user flags where the read bit can end up unset; version differences where a constant moved between classes.

Related errors


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