alibaba/spring-ai-alibaba · error · RuntimeException
RuntimeException wrapping transaction failure (no literal me
Error message
RuntimeException wrapping transaction failure (no literal message; wraps cause e)
What it means
Any failure inside release()'s Mongo transaction (query errors, transaction errors, network failures, duplicate release) is caught, the transaction is aborted, and the original exception is rethrown wrapped in a RuntimeException with no message of its own. The real cause is in getCause(), so debugging requires unwrapping it.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/mongo/MongoSaver.java:513
String checkpointDocId = CHECKPOINT_PREFIX + threadId;
Document checkpointDoc = checkpointCollection.find(clientSession, new BasicDBObject("_id", checkpointDocId))
.first();
Collection<Checkpoint> checkpoints = Collections.emptyList();
if (checkpointDoc != null) {
String checkpointsStr = checkpointDoc.getString(DOCUMENT_CONTENT_KEY);
if (checkpointsStr != null) {
checkpoints = deserializeCheckpoints(checkpointsStr);
}
}
clientSession.commitTransaction();
return new Tag(threadName, checkpoints);
}
catch (Exception e) {
clientSession.abortTransaction();
throw new RuntimeException(e);
}
finally {
clientSession.close();
}
}
/**
* Builder class for MongoSaver.
*/
public static class Builder {
private MongoClient client;
private StateSerializer stateSerializer;
public Builder client(MongoClient client) {
this.client = client;
return this;
}
View on GitHub (pinned to f82da0b50f)
Solutions
- Log the full cause: ex.getCause() (or print the whole stack trace) to find the root error
- Ensure MongoDB runs as a replica set (transactions require one); run as a single-node replica set in dev
- Catch RuntimeException around release() and unwrap getCause() to branch on specific root causes
- Enable driver logging to see the underlying Mongo command failure
Example fix
// before
catch (RuntimeException e) { log.error("release failed: " + e.getMessage()); }
// after
catch (RuntimeException e) {
Throwable cause = e.getCause() != null ? e.getCause() : e;
log.error("release failed", cause);
} Defensive patterns
Strategy: retry
Validate before calling
// pre-check: transactions require a replica set
boolean isReplicaSet = mongoClient.getClusterDescription()
.getServerDescriptions().stream()
.allMatch(sd -> sd.getType() == ServerType.REPLICA_SET_PRIMARY
|| sd.getType() == ServerType.REPLICA_SET_SECONDARY); Try / catch
try {
return saver.release(cfg);
} catch (RuntimeException e) {
Throwable root = e;
while (root.getCause() != null && root.getCause() != root) root = root.getCause();
if (root instanceof MongoTransientTransactionException || root instanceof MongoSocketException) {
return retryWithBackoff(() -> saver.release(cfg));
}
throw root;
} Prevention
- Always log the full stack trace (not just getMessage()) for this wrapper
- Run MongoDB as a replica set — standalone servers reject transactions
- Use retry with backoff for transient transaction/network failures
- Keep release() call paths free of other exceptions so the cause is the true root
When it happens
Trigger: Any exception thrown between startTransaction() and commitTransaction() in release(): Mongo query failures, write conflicts, network drops, driver errors, or the IllegalStateExceptions thrown earlier in the method being caught by this same catch block.
Common situations: MongoDB deployments without replica sets (transactions unsupported) — the commit/startTransaction fails; transient network issues; wrong database credentials surfacing as wrapped exceptions; generic catch blocks in user code logging only e.getMessage() and seeing 'null'.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/7da629e552c6c05f.
Report an issue: GitHub.