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
- Always pass the thread id to the constructor: new Thread.Builder("my-thread-id") instead of the no-arg builder.
- If deserializing from JSON, ensure the payload contains the threadId property ("threadId" per @JsonProperty) before build() is invoked.
- Guard the id before building: check for null/blank and fail early with a clear message at the call site.
- 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
- Prefer the Thread.Builder(String threadId) constructor over the no-arg builder.
- When deserializing JSON, validate that the threadId property is present.
- Generate ids (UUID) before constructing builders, never inside/after build().
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
- ChatClient must not be null
- ${type} requires valid configuration
- Version ID cannot be null
- Result is null, cannot extract response text
- Name must be provided
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/b13ce3e9f5753568.
Report an issue: GitHub.