alibaba/spring-ai-alibaba · error · ResponseStatusException

threadId cannot be null or empty

Error message

threadId cannot be null or empty

What it means

The SSE run endpoint rejects requests whose threadId is null or blank. Spring AI Alibaba's ExecutionController requires a threadId to identify the conversation/checkpoint stream, so it returns a 400 ResponseStatusException wrapped in a Flux error before any agent loading happens.

Source

Thrown at spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/controller/ExecutionController.java:171

	 * @return A Flux that will stream events to the client in standard SSE format.
	 */
	@PostMapping(value = "/run_sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
	public Flux<ServerSentEvent<String>> agentRunSse(@RequestBody AgentRunRequest request) {
		if (request.appName == null || request.appName.trim().isEmpty()) {
			log.warn(
					"appName cannot be null or empty in SSE request for appName: {}, session: {}",
					request.appName,
					request.threadId);
			return Flux.error(
					new ResponseStatusException(HttpStatus.BAD_REQUEST, "appName cannot be null or empty"));
		}
		if (request.threadId == null || request.threadId.trim().isEmpty()) {
			log.warn(
					"threadId cannot be null or empty in SSE request for appName: {}, session: {}",
					request.appName,
					request.threadId);
			return Flux.error(
					new ResponseStatusException(HttpStatus.BAD_REQUEST, "threadId cannot be null or empty"));
		}

		try {
			Agent agent = agentLoader.loadAgent(request.appName);
			RunnableConfig runnableConfig = RunnableConfig.builder()
					.threadId(request.threadId)
					.addMetadata("user_id", request.userId)
					.build();

			return executeAgent(request.newMessage.toUserMessage(), agent, runnableConfig);
		}
		catch (Exception e) {
			log.error("Error during agent run for session {}", request.threadId, e);
			return Flux.error(new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Agent run failed", e));
		}
	}

	@PostMapping(value = "/resume_sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Set a non-empty threadId in the AgentRunRequest body before calling the endpoint
  2. Generate a threadId client-side (e.g. UUID) when starting a new conversation
  3. Validate the request payload on the client before opening the SSE connection

Example fix

// before
POST /run_sse {"appName":"chatbot"}
// after
POST /run_sse {"appName":"chatbot","threadId":"a1b2c3"}
Defensive patterns

Strategy: validation

Validate before calling

if (req.threadId == null || req.threadId.trim().isEmpty()) { throw new IllegalArgumentException("threadId required"); }

Type guard

boolean hasThreadId(AgentRunRequest r) { return r != null && r.threadId() != null && !r.threadId().trim().isEmpty(); }

Try / catch

try { flux.subscribe(...) } catch (ResponseStatusException e) { if (e.getStatusCode() == HttpStatus.BAD_REQUEST) { /* fix request fields */ } }

Prevention

When it happens

Trigger: POSTing to /execution/run_sse with a JSON body whose threadId field is absent, null, or whitespace-only.

Common situations: Client SDKs omitting threadId when starting a run; frontend state not yet initialized before opening the SSE stream; serialized request objects dropping empty strings; API version changes renaming the field.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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