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

  1. Read/extract the XML content (e.g. via getString()) before calling free()
  2. Never store SQLXML objects beyond the statement/transaction in which they were produced
  3. Remove duplicate free() calls, or restructure code so cleanup happens after all reads
  4. 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

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.