{"record":{"id":"8fe0745ed1c17459","repo":"alibaba/spring-ai-alibaba","slug":"thread-found-but-belongs-to-a-different-app-user","errorCode":null,"errorMessage":"Thread found but belongs to a different app/user.","messagePattern":"Thread found but belongs to a different app/user\\.","errorType":"http","errorClass":"ResponseStatusException","httpStatus":404,"severity":"error","filePath":"spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/controller/ThreadController.java","lineNumber":99,"sourceCode":"\t\t\t\t\tHttpStatus.NOT_FOUND,\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Thread not found: appName=%s, userId=%s, threadId=%s\",\n\t\t\t\t\t\t\tappName, userId, threadId));\n\t\t}\n\n\t\tThread thread = optionalThread.get();\n\n\t\tif (!Objects.equals(thread.appName(), appName) || !Objects.equals(thread.userId(), userId)) {\n\t\t\tlog.warn(\n\t\t\t\t\t\"Thread ID {} found but appName/userId mismatch (Expected: {}/{}, Found: {}/{}) -\"\n\t\t\t\t\t\t\t+ \" Treating as not found.\",\n\t\t\t\t\tthreadId,\n\t\t\t\t\tappName,\n\t\t\t\t\tuserId,\n\t\t\t\t\tthread.appName(),\n\t\t\t\t\tthread.userId());\n\n\t\t\tthrow new ResponseStatusException(\n\t\t\t\t\tHttpStatus.NOT_FOUND, \"Thread found but belongs to a different app/user.\");\n\t\t}\n\t\tlog.debug(\"Found thread: {}\", threadId);\n\t\treturn thread;\n\t}\n\n\t/**\n\t * Retrieves a specific thread by its ID.\n\t *\n\t * @param appName The application name.\n\t * @param userId The user ID.\n\t * @param threadId The thread ID.\n\t * @return The requested Thread object.\n\t * @throws ResponseStatusException if the thread is not found.\n\t */\n\t@GetMapping(\"/apps/{appName}/users/{userId}/threads/{threadId}\")\n\tpublic Thread getThread(\n\t\t\t@PathVariable String appName, @PathVariable String userId, @PathVariable String threadId) {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-studio/src/main/java/com/alibaba/cloud/ai/agent/studio/controller/ThreadController.java#L81-L117","documentation":"findThreadOrThrow also throws 404 NOT_FOUND ('Thread found but belongs to a different app/user.') when a Thread record with the given threadId exists but its stored appName or userId differs from the path variables. Threads are namespaced by (appName, userId, threadId), so a match on ID alone is deliberately treated as not found to avoid cross-tenant leakage.","triggerScenarios":"Requesting the same threadId under a different userId or appName than the one that created it; reusing a client-generated threadId (e.g. hardcoded or session-derived) across users; copy-pasting a URL between environments or accounts.","commonSituations":"Multi-user apps reusing the same session identifier for every user; load balancer routing to a different studio instance where another user created the same ID; tests hardcoding threadId while varying userId.","solutions":["Use the exact appName and userId that were used when the thread was created","Make client-generated thread IDs unique per user (append userId or use a UUID)","List the user's threads (GET /apps/{appName}/users/{userId}/threads) to find the correct threadId","Create a new thread under the correct (appName, userId) pair instead of reusing the ID","Check for proxy/environment mixing that points the client at another deployment's data"],"exampleFix":"// before\nString threadId = \"main-session\"; // same for all users\n// after\nString threadId = userId + \"-main-session\"; // namespaced per user","handlingStrategy":"validation","validationCode":"// ensure thread belongs to this user before fetching\nString scopedId = userId + \"-\" + sessionId;\ntry { GET \"/apps/{a}/users/{u}/threads/\" + scopedId; } catch (NotFound e) { createThread(scopedId); }","typeGuard":null,"tryCatchPattern":"try { return getThread(app, user, id); } catch (HttpClientErrorException.NotFound e) { /* wrong owner or missing: create under this user */ return createThread(app, user, id); }","preventionTips":["Namespace client-generated thread IDs with userId","Never share threadId values across users or deployments","Store (appName, userId, threadId) together in client session state","Verify multi-user routing does not mix studio instances"],"tags":["http","not-found","multi-tenancy","thread-management"],"backgroundTag":"resource-not-found","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"}