flowable/flowable-engine · error · java.sql.SQLException
Cannot unwrap as an instance of
Error message
Cannot unwrap ${getClass().getName()} as an instance of ${iface.getName()} What it means
TenantAwareDataSource.unwrap() only supports unwrapping to the TenantAwareDataSource class itself; it does not delegate to the underlying per-tenant DataSource. If a caller requests any other interface, it throws SQLException. This is a deliberate (minimal) JDBC Wrapper implementation.
Solutions
- Unwrap via isWrapperFor check only for TenantAwareDataSource.class, or obtain the underlying DataSource directly from the tenant map instead of unwrapping
- Extend/wrap TenantAwareDataSource with a delegating unwrap() that forwards to the current tenant's DataSource if you need vendor interfaces
- Check the object with isWrapperFor(iface) first and branch your code accordingly
- Avoid vendor-specific calls through the tenant-aware layer; use the raw DataSource per tenant for those operations
Example fix
// before
OracleDataSource ods = tenantAwareDs.unwrap(OracleDataSource.class);
// after
DataSource current = tenantAwareDs.getDataSource(tenantInfoHolder.getCurrentTenantId());
if (current.isWrapperFor(OracleDataSource.class)) {
OracleDataSource ods = current.unwrap(OracleDataSource.class);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!tenantAwareDs.isWrapperFor(iface)) {
throw new SQLException(iface + " not supported by TenantAwareDataSource");
} Type guard
boolean isTenantAwareDs(DataSource ds) {
return ds instanceof TenantAwareDataSource;
}
// only unwrap TenantAwareDataSource.class from it Try / catch
try {
T unwrapped = ds.unwrap(iface);
} catch (SQLException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Cannot unwrap")) {
// fall back to the underlying per-tenant DataSource
}
} Prevention
- Never request vendor-specific interfaces from the tenant-aware wrapper; unwrap the underlying DataSource instead
- Check isWrapperFor() before calling unwrap()
- Remember TenantAwareDataSource only supports unwrapping to itself
When it happens
Trigger: Calling unwrap(SomeInterface.class) on a TenantAwareDataSource where SomeInterface is not the TenantAwareDataSource type — e.g. vendor-specific interfaces like OracleDataSource, pool-specific types, or JDBC 4 Wrapper lookups by connection-pool tooling.
Common situations: Framework code (connection pool probes, monitoring, spring DataSource wrappers) probing for vendor extensions; code expecting unwrap to pass through to the underlying tenant DataSource; migrating code that previously used a native DataSource directly.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Could not find a dataSource for tenant
- DataSource or JDBC properties have to be specified in a…
- DataSource or JDBC properties have to be specified in a…
- A datasource is required for initializing the engine
- Could not retrieve database metadata:
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/69ec316e25d69a54.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/cfg/multitenant/TenantAwareDataSource.java:89
}
@Override
public int getLoginTimeout() throws SQLException {
return 0; // Default
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
@SuppressWarnings("unchecked")
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this)) {
return (T) this;
}
throw new SQLException("Cannot unwrap " + getClass().getName() + " as an instance of " + iface.getName());
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return iface.isInstance(this);
}
public Map<Object, DataSource> getDataSources() {
return dataSources;
}
public void setDataSources(Map<Object, DataSource> dataSources) {
this.dataSources = dataSources;
}
// Unsupported //////////////////////////////////////////////////////////
@OverrideView on GitHub (pinned to d6d39ce1c6)