alibaba/spring-ai-alibaba · error · IllegalStateException

Thread id is null

Error message

Thread id is null

What it means

Thrown by Thread.Builder.build() as a final invariant check: a Thread instance must always have a non-null threadId. The builder was constructed without a threadId (e.g., via the Jackson @JsonCreator no-arg constructor) and no threadId(...) setter was called before build(). It signals a programmer/marshalling error, not a runtime condition.

Source

Thrown at spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/dto/Thread.java:122

		}

		@CanIgnoreReturnValue
		@JsonProperty("userId")
		public Builder userId(String userId) {
			this.userId = userId;
			return this;
		}

		@CanIgnoreReturnValue
		@JsonProperty("values")
		public Builder values(Map<String, MessageDTO> values) {
			this.values = values;
			return this;
		}

		public Thread build() {
			if (threadId == null) {
				throw new IllegalStateException("Thread id is null");
			}
			return new Thread(threadId, appName, userId, values);
		}
	}
}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Always pass the thread id to the constructor: new Thread.Builder("my-thread-id") instead of the no-arg builder.
  2. If deserializing from JSON, ensure the payload contains the threadId property ("threadId" per @JsonProperty) before build() is invoked.
  3. Guard the id before building: check for null/blank and fail early with a clear message at the call site.
  4. If the id is generated, generate it before constructing the builder (e.g., UUID.randomUUID().toString()) rather than leaving it unset.

Example fix

// before
Thread.Builder b = new Thread.Builder(); // no id set
b.appName(app).userId(user).values(vals);
Thread t = b.build(); // IllegalStateException: Thread id is null

// after
String id = UUID.randomUUID().toString();
Thread t = new Thread.Builder(id).appName(app).userId(user).values(vals).build();
Defensive patterns

Strategy: validation

Validate before calling

if (threadId == null || threadId.isBlank()) {
    throw new IllegalArgumentException("threadId must be a non-blank string before building Thread");
}

Type guard

static boolean hasThreadId(Thread.Builder b, String id) { return id != null && !id.isBlank(); }

Try / catch

try {
    Thread t = builder.build();
} catch (IllegalStateException e) {
    // threadId missing: log payload, supply/generated an id, and rebuild
}

Prevention

When it happens

Trigger: Calling new Thread.Builder(null) or new Thread.Builder(id).threadId(null), then build(); deserializing JSON into the Builder via the no-arg @JsonCreator constructor when the JSON lacks "threadId" (or the thread_id property), then invoking build(); programmatically forgetting the mandatory threadId(...) setter.

Common situations: Hand-rolled Jackson deserialization binding JSON payloads missing the threadId field into the builder; refactoring code that previously passed a literal id into the constructor but now uses the no-arg builder path; copy-paste code creating threads in tests without setting the id.

Related errors


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