alibaba/spring-ai-alibaba · error · IllegalArgumentException

threadId isn't allow null

Error message

threadId isn't allow null

What it means

RedisSaver.get(CheckpointSaver.RunnableConfig) requires a threadId in the config; if Optional<String> threadId() is empty it throws IllegalArgumentException('threadId isn\u2019t allow null') before acquiring the Redis lock. (This method backs checkpoint(...) and retrieved(...) flows.)

Source

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

		}
		catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		catch (IOException | ClassNotFoundException e) {
			throw new RuntimeException("Failed to deserialize checkpoints", e);
		}
		finally {
			if (lock.isHeldByCurrentThread()) {
				lock.unlock();
			}
		}
	}

	@Override
	public Optional<Checkpoint> get(RunnableConfig config) {
		Optional<String> threadNameOpt = config.threadId();
		if (!threadNameOpt.isPresent()) {
			throw new IllegalArgumentException("threadId isn't allow null");
		}

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

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

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Always set threadId when building RunnableConfig for checkpoint operations.
  2. Guard calls: if (config.threadId().isEmpty()) throw/skip before invoking get().
  3. Ensure your stateful runnable assigns a threadId at start (e.g. via RunnableConfig with a stable session id).

Example fix

// before
Optional<Checkpoint> cp = saver.get(RunnableConfig.builder().build());
// after
RunnableConfig config = RunnableConfig.builder().threadId(sessionId).build();
Optional<Checkpoint> cp = saver.get(config);
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

try { saver.get(config); } catch (IllegalArgumentException e) { if (e.getMessage().contains("threadId")) throw new IllegalStateException("Set threadId on RunnableConfig", e); throw e; }

Prevention

When it happens

Trigger: Calling saver.get(config) or code paths like checkpoint()/retrieved() with a RunnableConfig built without threadId — e.g. RunnableConfig.builder().build().

Common situations: Forgetting threadId when resuming a graph from a checkpoint; configs copied from a fresh invocation that never got an id; tests constructing minimal configs.

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/9500cbb07eee21e4. Report an issue: GitHub.