Tencent/matrix · error · IllegalStateException

Call wait() and check its result

Error message

Call wait() and check its result

What it means

FutureResult.get returns the computed result but requires wait() to have completed first; it checks latch.getCount() and throws IllegalStateException if the latch hasn't counted down to zero. This guards against reading resultHolder before the producer thread has set it, which would return null/garbage silently.

Solutions

  1. Always call wait(timeout, TimeUnit) and check its boolean result before calling get().
  2. If wait() returned false (timeout), handle the timeout instead of calling get().
  3. Use the same thread flow as the library's dumpHeap: wait, then get.
  4. If you need async semantics, wrap wait+get in your own future/callback rather than splitting them across threads.

Example fix

// before
FutureResult<HeapDump> fr = dumpHeapAsync();
HeapDump dump = fr.get();
// after
FutureResult<HeapDump> fr = dumpHeapAsync();
if (fr.wait(10, TimeUnit.SECONDS)) {
    HeapDump dump = fr.get();
}
Defensive patterns

Strategy: validation

Validate before calling

if (!futureResult.wait(10, TimeUnit.SECONDS)) {
    throw new TimeoutException("heap dump did not finish");
}
T result = futureResult.get();

Try / catch

try {
    T result = futureResult.get();
} catch (IllegalStateException e) {
    // wait() was skipped or timed out; redo wait or abort
}

Prevention

When it happens

Trigger: Calling get() on a FutureResult without first calling wait(timeout, unit) and confirming it returned true, e.g. calling get() immediately after enqueueing the heap dump task.

Common situations: Misusing FutureResult as a plain holder; forgetting the wait step after copying sample code; calling get() from a different thread that skipped the wait timeout.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/f7d372be7eccb1df. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-android/src/main/java/com/tencent/matrix/resource/leakcanary/internal/FutureResult.java:42

    private final AtomicReference<T> resultHolder;
    private final CountDownLatch latch;

    public FutureResult() {
        resultHolder = new AtomicReference<>();
        latch = new CountDownLatch(1);
    }

    public boolean wait(long timeout, TimeUnit unit) {
        try {
            return latch.await(timeout, unit);
        } catch (InterruptedException e) {
            throw new RuntimeException("Did not expect thread to be interrupted", e);
        }
    }

    public T get() {
        if (latch.getCount() > 0) {
            throw new IllegalStateException("Call wait() and check its result");
        }
        return resultHolder.get();
    }

    public void set(T result) {
        resultHolder.set(result);
        latch.countDown();
    }
}

View on GitHub (pinned to 3b8293bd65)