karatelabs/karate · error · IllegalStateException
store has been closed
Error message
store has been closed
What it means
DiskBackedList.get() throws this IllegalStateException when the list's backing store has already been closed via close(). After close, the temp file and RandomAccessFile handles are released, so reads are rejected explicitly rather than failing obscurely. This is a use-after-close guard.
Solutions
- Do not close the list until all reads (get/iterator) are complete
- If you need the data after close, copy it into a regular List before closing
- Restructure so the consumer owns the list lifecycle (close where it was created)
- Guard concurrent access: ensure close happens only after all reader threads finish
Example fix
// before
try (DiskBackedList list = new DiskBackedList(...)) { fill(list); }
return list; // closed!
// after
DiskBackedList list = new DiskBackedList(...);
fill(list);
return list; // caller closes Defensive patterns
Strategy: try-catch
Validate before calling
if (list instanceof DiskBackedList) {
// cannot query closed state publicly; ensure close happens after use
useList(list);
((DiskBackedList) list).close();
} Type guard
boolean isClosed(DiskBackedList l) { try { l.size(); return false; } catch (IllegalStateException e) { return true; } } Try / catch
try { return list.get(i); } catch (IllegalStateException e) { if (e.getMessage().equals("store has been closed")) throw new IllegalStateException("read after close; keep list open until consumers finish", e); throw e; } Prevention
- Close the list only after all reads complete
- Never return a list managed by try-with-resources
- Copy to an in-memory list if data must outlive the store
- Document ownership of the list lifecycle
When it happens
Trigger: Calling get(index) on a DiskBackedList after calling close(); using a list that was closed by a try-with-resources block or shutdown hook; sharing the list across threads where one thread closes while another reads.
Common situations: Returning a DiskBackedList from a method that used try-with-resources; closing in a finally block then iterating later; lifecycle bugs in large-result-set handling where results are lazily read from disk.
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
- timeout waiting for stream readers - this should not happen
- Failed to read bytes from
- karate.driver can only be read within a scenario
- channel() can only be called within a scenario
- karate.setup() is not available in this context
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/f696e9f400cabba9.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/match/DiskBackedList.java:179
return JSONValue.toJSONString(item);
}
private static Object deserializeItem(String json) {
if (json == null || json.isEmpty() || "null".equals(json)) {
return null;
}
return JSONValue.parse(json);
}
@Override
public int size() {
return size;
}
@Override
public Object get(int index) {
if (closed) {
throw new IllegalStateException("store has been closed");
}
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("index: " + index + ", size: " + size);
}
try {
if (raf == null) {
raf = new RandomAccessFile(tempFile, "r");
}
long offset = lineOffsets.get(index);
raf.seek(offset);
String line = raf.readLine();
return deserializeItem(line);
} catch (IOException e) {
throw new RuntimeException("failed to read item at index " + index, e);
}
}
@OverrideView on GitHub (pinned to a22eb90246)