{"record":{"id":"db3c05fd118e7f14","repo":"alibaba/spring-ai-alibaba","slug":"thread-not-found-graphname-s-userid-s-threadi","errorCode":null,"errorMessage":"Thread not found: graphName=%s, userId=%s, threadId=%s","messagePattern":"Thread not found: graphName=(.+?), userId=(.+?), threadId=(.+?)","errorType":"http","errorClass":"ResponseStatusException","httpStatus":404,"severity":"error","filePath":"spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/controller/GraphThreadController.java","lineNumber":89,"sourceCode":"\t\treturn GRAPH_APP_PREFIX + graphName;\n\t}\n\n\tprivate void validateGraphExists(String graphName) {\n\t\tif (graphName == null || graphName.isBlank()) {\n\t\t\tthrow new ResponseStatusException(HttpStatus.BAD_REQUEST, \"graphName cannot be null or empty\");\n\t\t}\n\t\tif (!graphLoader.listGraphs().contains(graphName)) {\n\t\t\tthrow new ResponseStatusException(HttpStatus.NOT_FOUND, \"Graph not found: \" + graphName);\n\t\t}\n\t}\n\n\tprivate Thread findThreadOrThrow(String graphName, String userId, String threadId) {\n\t\tString appName = toAppName(graphName);\n\t\tOptional<Thread> optionalThread =\n\t\t\t\tthreadService.getThread(appName, userId, threadId, Optional.empty()).block();\n\n\t\tif (optionalThread == null || !optionalThread.isPresent()) {\n\t\t\tthrow new ResponseStatusException(HttpStatus.NOT_FOUND,\n\t\t\t\t\tString.format(\"Thread not found: graphName=%s, userId=%s, threadId=%s\",\n\t\t\t\t\t\t\tgraphName, userId, threadId));\n\t\t}\n\n\t\tThread thread = optionalThread.get();\n\t\tif (!Objects.equals(thread.appName(), appName) || !Objects.equals(thread.userId(), userId)) {\n\t\t\tthrow new ResponseStatusException(HttpStatus.NOT_FOUND,\n\t\t\t\t\t\"Thread found but belongs to a different graph/user.\");\n\t\t}\n\t\treturn thread;\n\t}\n\n\t@GetMapping(\"/graphs/{graphName}/users/{userId}/threads/{threadId}\")\n\tpublic Thread getThread(\n\t\t\t@PathVariable String graphName,\n\t\t\t@PathVariable String userId,\n\t\t\t@PathVariable String threadId) {\n\t\tvalidateGraphExists(graphName);","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/controller/GraphThreadController.java#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the threadId was created previously via the create-thread endpoint for the same graph and user.","Check userId and graphName match the ones used at thread creation.","Verify the thread persistence store is running and configured correctly (empty lookups can stem from backend failures).","List threads for the user to find valid threadIds."],"exampleFix":"// before\nconst t = await api.get(`/graphs/my-graph/users/u1/threads/${threadId}`); // never created\n// after\nconst created = await api.post(`/graphs/my-graph/users/u1/threads`, {});\nconst t = await api.get(`/graphs/my-graph/users/u1/threads/${created.threadId}`);","handlingStrategy":"validation","validationCode":"// before fetching a thread\nconst threads = await api.get(`/graphs/${graphName}/users/${userId}/threads`);\nif (!threads.some(t => t.threadId === threadId)) {\n  throw new Error(`Thread ${threadId} does not exist for this graph/user`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.get(`/graphs/${graphName}/users/${userId}/threads/${threadId}`);\n} catch (e) {\n  if (e.response?.status === 404) { /* create the thread or use a listed threadId */ }\n}","preventionTips":["Only use threadIds returned by thread-creation or list endpoints.","Keep (graphName, userId, threadId) triples consistent client-side.","Ensure the thread persistence backend is healthy and configured."],"tags":["http-404","rest","thread-not-found"],"backgroundTag":"record-not-found","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}