quarkusio/quarkus · error · PSQLException
OBJECT_NOT_IN_STATE
OBJECT_NOT_IN_STATE
Error message
This SQLXML object has already been freed.
What it means
SQLXML objects are one-shot: after free() is called, JDBC requires every other method to fail. PgSQLXML.checkFreed() enforces this by throwing PSQLException with SQLState OBJECT_NOT_IN_STATE whenever freed == true. It means you are using an SQLXML instance whose lifecycle has already been explicitly closed.
Source
Thrown at extensions/jdbc/jdbc-postgresql/runtime/src/main/java/io/quarkus/jdbc/postgresql/runtime/graal/PgSQLXML.java:281
}
}
throw new PSQLException(GT.tr("Unknown XML Result class: {0}", resultClass),
PSQLState.INVALID_PARAMETER_TYPE);
}
@Substitute
@Override
public synchronized void setString(String value) throws SQLException {
checkFreed();
initialize();
data = value;
}
@Substitute
private void checkFreed() throws SQLException {
if (freed) {
throw new PSQLException(GT.tr("This SQLXML object has already been freed."),
PSQLState.OBJECT_NOT_IN_STATE);
}
}
@Substitute
private void ensureInitialized() throws SQLException {
if (!initialized) {
throw new PSQLException(
GT.tr(
"This SQLXML object has not been initialized, so you cannot retrieve data from it."),
PSQLState.OBJECT_NOT_IN_STATE);
}
// Is anyone loading data into us at the moment?
if (!active) {
return;
}
View on GitHub (pinned to e1c734241f)
Solutions
- Read/extract the XML content (e.g. via getString()) before calling free()
- Never store SQLXML objects beyond the statement/transaction in which they were produced
- Remove duplicate free() calls, or restructure code so cleanup happens after all reads
- If you need the data later, copy it into a String/byte[]/DOM first
Example fix
// before
SQLXML xml = rs.getSQLXML("data");
xml.free();
String s = xml.getString(); // throws
// after
SQLXML xml = rs.getSQLXML("data");
String s = xml.getString();
xml.free(); Defensive patterns
Strategy: validation
Validate before calling
// Track lifecycle manually; JDBC has no isFreed accessor, so guard ownership yourself:
SQLXML xml = rs.getSQLXML("data");
String content = xml.getString(); // extract BEFORE any free()
xml.free(); Type guard
boolean usable(SQLXML xml) { try { xml.getString(); return true; } catch (SQLException e) { return false; } } // only in tests Try / catch
try {
String s = sqlxml.getString();
} catch (SQLException e) {
if (e.getMessage() != null && e.getMessage().contains("already been freed")) {
throw new IllegalStateException("SQLXML read after free()");
}
throw e;
} Prevention
- Extract content to String/byte[] before calling free()
- Never cache SQLXML objects beyond the ResultSet/transaction scope
- Call free() exactly once, as the last operation
- Use try-with-resources on SQLXML where supported to centralize cleanup
When it happens
Trigger: Calling any of getBinaryStream, getCharacterStream, getSource, getString, setBinaryStream, setCharacterStream, setResult, setString after sqlxml.free() has been invoked; or reusing a cached SQLXML object across iterations after freeing it.
Common situations: Keeping SQLXML references in long-lived collections or DTOs and reading them after cleanup code ran; calling free() in a finally block then reading the value again for logging; caching row data with SQLXML fields beyond the ResultSet scope.
Related errors
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/aac2c74c4eb5157c.
Report an issue: GitHub.