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

  1. Log the full cause: ex.getCause() (or print the whole stack trace) to find the root error
  2. Ensure MongoDB runs as a replica set (transactions require one); run as a single-node replica set in dev
  3. Catch RuntimeException around release() and unwrap getCause() to branch on specific root causes
  4. 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

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.