alibaba/spring-ai-alibaba · error · ResponseStatusException

Thread not found: graphName=

Error message

Thread not found: graphName=%s, userId=%s, threadId=%s

What it means

findThreadOrThrow looks up a thread by appName (derived from graphName), userId, and threadId via threadService.getThread(...).block(). If the result is absent it throws a 404 NOT_FOUND ResponseStatusException with a formatted message naming all three keys.

Solutions

  1. Confirm the threadId was created previously via the create-thread endpoint for the same graph and user.
  2. Check userId and graphName match the ones used at thread creation.
  3. Verify the thread persistence store is running and configured correctly (empty lookups can stem from backend failures).
  4. List threads for the user to find valid threadIds.

Example fix

// before
const t = await api.get(`/graphs/my-graph/users/u1/threads/${threadId}`); // never created
// after
const created = await api.post(`/graphs/my-graph/users/u1/threads`, {});
const t = await api.get(`/graphs/my-graph/users/u1/threads/${created.threadId}`);
Defensive patterns

Strategy: validation

Validate before calling

// before fetching a thread
const threads = await api.get(`/graphs/${graphName}/users/${userId}/threads`);
if (!threads.some(t => t.threadId === threadId)) {
  throw new Error(`Thread ${threadId} does not exist for this graph/user`);
}

Try / catch

try {
  await api.get(`/graphs/${graphName}/users/${userId}/threads/${threadId}`);
} catch (e) {
  if (e.response?.status === 404) { /* create the thread or use a listed threadId */ }
}

Prevention

When it happens

Trigger: GET /graphs/{graphName}/users/{userId}/threads/{threadId} or createThreadWithId where no thread exists with that exact (graphName→appName, userId, threadId) triple, or the blocking lookup returns empty/null.

Common situations: Thread was already deleted; wrong threadId (e.g. client-generated ID never created); thread belongs to a different user; persistence backend (Mongo/Redis/etc.) not reachable so lookups return empty; retrying with an ID from another environment.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

		return GRAPH_APP_PREFIX + graphName;
	}

	private void validateGraphExists(String graphName) {
		if (graphName == null || graphName.isBlank()) {
			throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "graphName cannot be null or empty");
		}
		if (!graphLoader.listGraphs().contains(graphName)) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Graph not found: " + graphName);
		}
	}

	private Thread findThreadOrThrow(String graphName, String userId, String threadId) {
		String appName = toAppName(graphName);
		Optional<Thread> optionalThread =
				threadService.getThread(appName, userId, threadId, Optional.empty()).block();

		if (optionalThread == null || !optionalThread.isPresent()) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND,
					String.format("Thread not found: graphName=%s, userId=%s, threadId=%s",
							graphName, userId, threadId));
		}

		Thread thread = optionalThread.get();
		if (!Objects.equals(thread.appName(), appName) || !Objects.equals(thread.userId(), userId)) {
			throw new ResponseStatusException(HttpStatus.NOT_FOUND,
					"Thread found but belongs to a different graph/user.");
		}
		return thread;
	}

	@GetMapping("/graphs/{graphName}/users/{userId}/threads/{threadId}")
	public Thread getThread(
			@PathVariable String graphName,
			@PathVariable String userId,
			@PathVariable String threadId) {
		validateGraphExists(graphName);

View on GitHub (pinned to f82da0b50f)