Tencent/tinker · error · IllegalArgumentException

obj: {} cannot be closed.

Error message

obj: {} cannot be closed.

What it means

SharePatchFileUtil.closeQuietly only closes objects that are AutoCloseable or ZipFile; any other object triggers IllegalArgumentException('obj: X cannot be closed.'). This is a developer-programming guard: the utility refuses to pretend it closed something it has no idea how to close.

Source

Thrown at tinker-android/tinker-android-loader-no-op/src/main/java/com/tencent/tinker/loader/shareutil/SharePatchFileUtil.java:162

            try {
                ((Closeable) obj).close();
            } catch (Throwable ignored) {
                // Ignored.
            }
        } else if (Build.VERSION.SDK_INT >= 19 && obj instanceof AutoCloseable) {
            try {
                ((AutoCloseable) obj).close();
            } catch (Throwable ignored) {
                // Ignored.
            }
        } else if (obj instanceof ZipFile) {
            try {
                ((ZipFile) obj).close();
            } catch (Throwable ignored) {
                // Ignored.
            }
        } else {
            throw new IllegalArgumentException("obj: " + obj + " cannot be closed.");
        }
    }

    public static final boolean isLegalFile(File file) {
        return file != null && file.exists() && file.canRead() && file.isFile() && file.length() > 0;
    }

    /**
     * For some special device whose dex2oat procedure is optimized for tinker. (e.g. vivo, oppo)
     *
     * Because these devices by-pass our dex2oat request, which cause vm to load tinker's dex with interpret-mode
     * and generate nothing instead of a valid oat file. It's fine to skip the check so far.
     *
     * @param file
     * @return
     */
    public static final boolean shouldAcceptEvenIfIllegal(File file) {
        final boolean isSpecialManufacturer =

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Pass the actual resource object (InputStream/OutputStream/RandomAccessFile/ZipFile — all AutoCloseable), not a wrapper or owner.
  2. If the class only has a bespoke close(), call it directly in your own try/finally instead of closeQuietly.
  3. Wrap the non-AutoCloseable resource in a small adapter implementing AutoCloseable that delegates to its close method.

Example fix

// before
SharePatchFileUtil.closeQuietly(someLegacyHandle); // IllegalArgumentException

// after
try {
    someLegacyHandle.disconnect(); // its real release method
} catch (Throwable ignored) { }
Defensive patterns

Strategy: validation

Validate before calling

if (!(obj instanceof AutoCloseable) && !(obj instanceof ZipFile)) {
    throw new IllegalArgumentException("Not closeable: " + obj);
}
SharePatchFileUtil.closeQuietly(obj);

Type guard

static boolean isCloseable(Object o) { return o instanceof AutoCloseable || o instanceof java.util.zip.ZipFile; }

Prevention

When it happens

Trigger: Calling closeQuietly(obj) with an object that implements neither AutoCloseable nor ZipFile — e.g. a legacy class exposing only a custom close()/release()/destroy() method, a plain wrapper, or a null-ish sentinel passed by mistake.

Common situations: Copy-pasting the closeQuietly call around streams from older APIs that pre-date AutoCloseable (some dalvik-era or vendor classes); passing a domain object instead of the resource it owns.

Related errors


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