{"record":{"id":"97ccbabd4b8fe17e","repo":"alibaba/spring-ai-alibaba","slug":"thread-already-exists-97ccba","errorCode":null,"errorMessage":"Thread already exists: ","messagePattern":"Thread already exists: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/service/ThreadServiceImpl.java","lineNumber":90,"sourceCode":"\t\t\treturn ListThreadsResponse.of(userThreads);\n\t\t});\n\t}\n\n\t@Override\n\tpublic Mono<Thread> createThread(\n\t\t\tString appName, String userId, Map<String, Object> initialState, String threadId) {\n\t\treturn Mono.fromCallable(() -> {\n\t\t\t// Generate thread ID if not provided\n\t\t\tString finalThreadId = (threadId == null || threadId.trim().isEmpty())\n\t\t\t\t\t? generateThreadId()\n\t\t\t\t\t: threadId;\n\n\t\t\tString key = buildKey(appName, userId, finalThreadId);\n\n\t\t\t// Check if thread already exists\n\t\t\tif (threads.containsKey(key)) {\n\t\t\t\tlog.warn(\"Attempted to create duplicate thread: {}\", finalThreadId);\n\t\t\t\tthrow new IllegalStateException(\"Thread already exists: \" + finalThreadId);\n\t\t\t}\n\n\t\t\t// Create new thread\n\t\t\tThread newThread = Thread.builder(finalThreadId)\n\t\t\t\t\t.appName(appName)\n\t\t\t\t\t.userId(userId)\n\t\t\t\t\t.build();\n\n\t\t\tthreads.put(key, newThread);\n\n\t\t\t// Store initial state if provided\n\t\t\tif (initialState != null && !initialState.isEmpty()) {\n\t\t\t\tthradStates.put(key, new ConcurrentHashMap<>(initialState));\n\t\t\t}\n\n\t\t\tlog.info(\"Created thread: {} for app={}, user={}\", finalThreadId, appName, userId);\n\t\t\treturn newThread;\n\t\t});","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/service/ThreadServiceImpl.java#L72-L108","documentation":"ThreadServiceImpl.createThread builds a map key from appName/userId/threadId and throws IllegalStateException when the in-memory threads map already contains that key. The library enforces thread-ID uniqueness per app/user, refusing to silently overwrite an existing conversation thread. Callers must generate a fresh threadId or reuse the existing thread instead of calling createThread again.","triggerScenarios":"Calling ThreadService.createThread with (appName, userId, threadId) triple whose key already exists in the threads map — i.e., a second createThread call with the same threadId for the same app and user.","commonSituations":"Client retry after a timeout re-sends the same create request; a caller that saved the threadId and re-runs initialization code on restart; frontend generating a fixed threadId instead of a UUID per conversation; race where two requests create the same threadId concurrently (the warn log 'Attempted to create duplicate thread' indicates this path).","solutions":["Generate a unique threadId (e.g., UUID.randomUUID()) per conversation before calling createThread.","Check thread existence first via a get/threads lookup (or the same buildKey containsKey check) and reuse the existing thread instead of creating.","If retrying after failure, use the same threadId only when the previous create truly succeeded, otherwise issue a new threadId.","Wrap the call in try-catch for IllegalStateException and treat it as 'thread already initialized' by fetching the existing thread."],"exampleFix":"// before\nthreadService.createThread(appName, userId, \"main-thread\"); // throws on restart/retry\n// after\nString threadId = UUID.randomUUID().toString();\nthreadService.createThread(appName, userId, threadId);","handlingStrategy":"try-catch","validationCode":"if (threadService.getThread(appName, userId, threadId) != null) { /* reuse existing */ }","typeGuard":null,"tryCatchPattern":"try {\n    threadService.createThread(appName, userId, threadId);\n} catch (IllegalStateException e) {\n    if (e.getMessage().startsWith(\"Thread already exists\")) {\n        // reuse existing thread\n    } else { throw e; }\n}","preventionTips":["Always generate threadId with UUID.randomUUID() per conversation","Check thread existence before creating","Make create calls idempotent on the caller side so retries don't re-create"],"tags":["duplicate-resource","state","thread-management"],"backgroundTag":"file-already-exists","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}