alibaba/spring-ai-alibaba · error · IllegalArgumentException

threadId is not allow null

Error message

threadId is not allow null

What it means

RedisSaver.list(CheckpointSaver.RunnableConfig) requires the config to carry a threadId, since checkpoints are keyed by thread in Redis. If Optional<String> threadId() is empty it throws this IllegalArgumentException before touching Redis.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/checkpoint/savers/redis/RedisSaver.java:227

	private String getActiveThreadId(String threadName) {
		String metaKey = THREAD_META_PREFIX + threadName;
		RMap<String, String> meta = redisson.getMap(metaKey);

		String threadId = meta.get(FIELD_THREAD_ID);
		String isReleased = meta.get(FIELD_IS_RELEASED);

		if (threadId != null && !"true".equals(isReleased)) {
			return threadId;
		}

		return null; // No active thread exists
	}

	@Override
	public Collection<Checkpoint> list(RunnableConfig config) {
		Optional<String> threadNameOpt = config.threadId();
		if (!threadNameOpt.isPresent()) {
			throw new IllegalArgumentException("threadId is not allow null");
		}

		String threadName = threadNameOpt.get();
		RLock lock = redisson.getLock(LOCK_PREFIX + threadName);
		boolean tryLock = false;
		try {
			// 500ms timeout for read operations (list)
			tryLock = lock.tryLock(500, TimeUnit.MILLISECONDS);
			if (!tryLock) {
				return List.of();
			}

			// Get active thread_id for the thread_name
			String threadId = getActiveThreadId(threadName);
			if (threadId == null) {
				return List.of();
			}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Build the config with a threadId: RunnableConfig.builder().threadId("my-thread").build().
  2. Check config.threadId().isPresent() before calling list() and fall back to a default thread id.
  3. Trace where the RunnableConfig originates and ensure threadId is set at graph/stream invocation time.

Example fix

// before
saver.list(RunnableConfig.builder().build());
// after
saver.list(RunnableConfig.builder().threadId(threadId).build());
Defensive patterns

Strategy: validation

Validate before calling

if (config == null || config.threadId().isEmpty()) {
    throw new IllegalStateException("RunnableConfig must carry a threadId before list()");
}

Try / catch

try { saver.list(config); } catch (IllegalArgumentException e) { if (e.getMessage().contains("threadId")) { config = RunnableConfig.builder().threadId(defaultId).build(); saver.list(config); } else throw e; }

Prevention

When it happens

Trigger: Calling saver.list(RunnableConfig.builder().build()) or building a RunnableConfig without .threadId("...").

Common situations: Constructing RunnableConfig programmatically and forgetting the threadId; propagating a config from a graph run that never had an explicit thread id; refactored code where threadId assignment was dropped.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/ee68b07004da457b. Report an issue: GitHub.