{"record":{"id":"066e0c3bc0f4f5e0","repo":"xpzouying/xiaohongshu-mcp","slug":"tab-q-q","errorCode":null,"errorMessage":"当前 tab 为 %q，与请求的 %q 不符","messagePattern":"当前 tab 为 %q，与请求的 %q 不符","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"xiaohongshu/user_profile.go","lineNumber":123,"sourceCode":"\t\treturn nil, fmt.Errorf(\"failed to unmarshal userPageData: %w\", err)\n\t}\n\n\tvar notesData struct {\n\t\tNotes [][]Feed `json:\"notes\"`\n\t\tIndex int      `json:\"index\"`\n\t\tQuery string   `json:\"query\"`\n\t}\n\tif err := json.Unmarshal([]byte(notesResult), &notesData); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal notes: %w\", err)\n\t}\n\n\t// tab 不符时报错，避免把别的 tab 的内容当成结果返回\n\twant := tab\n\tif want == \"\" {\n\t\twant = TabNotes\n\t}\n\tif notesData.Query != \"\" && ProfileTab(notesData.Query) != want {\n\t\treturn nil, fmt.Errorf(\"当前 tab 为 %q，与请求的 %q 不符\", notesData.Query, want)\n\t}\n\n\t// 组装响应\n\tresponse := &UserProfileResponse{\n\t\tUserBasicInfo: userPageData.BasicInfo,\n\t\tInteractions:  userPageData.Interactions,\n\t}\n\n\t// 每个 tab 的内容存在各自的下标里，只取当前 tab 的，避免混入其他 tab\n\tif notesData.Index >= 0 && notesData.Index < len(notesData.Notes) {\n\t\tresponse.Feeds = append(response.Feeds, notesData.Notes[notesData.Index]...)\n\t}\n\n\treturn response, nil\n}\n\nfunc makeUserProfileURL(userID, xsecToken string, tab ProfileTab) string {\n\turl := fmt.Sprintf(\"https://www.xiaohongshu.com/user/profile/%s?xsec_token=%s&xsec_source=pc_note\", userID, xsecToken)","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/xiaohongshu/user_profile.go#L105-L141","documentation":"extractUserProfileData 的一致性守卫：页面 __INITIAL_STATE__ 中 activeTab 的 query 表明当前渲染的 tab 与请求的 tab（默认 note）不一致，说明 tab 切换没有成功，直接返回会错把别的 tab 的笔记当结果。","triggerScenarios":"调用 UserProfile / GetMyProfile 请求某个 tab，但页面当前停留的 tab 不同——例如前一次调用切换到了收藏 tab 后页面未复位，再次请求 note tab 时 activeTab.query 仍是 \"fav\"；或并发调用共用同一个 page 导致 tab 竞态。","commonSituations":"复用同一个 rod.Page 连续请求不同 tab，页面状态残留；并发请求不同 tab 互相覆盖页面状态；SPA 内部点击失败导致实际 tab 未切换。","solutions":["不要并发复用同一个 page 请求不同 tab，串行化或为每次请求新建 page/tab","每次请求前显式切换/复位到目标 tab","升级库版本看是否已加入 tab 复位逻辑","捕获此错误后重建页面再重试一次"],"exampleFix":"// before\nres1, _ := client.UserProfile(ctx, userID, \"fav\")\nres2, err := client.UserProfile(ctx, userID, \"note\") // tab 残留为 fav\n// after\nmu.Lock()\nres1, _ := client.UserProfile(ctx, userID, \"fav\")\nmu.Unlock()\nmu.Lock()\nres2, err := client.UserProfile(ctx, userID, \"note\") // 串行避免 tab 竞态\nmu.Unlock()","handlingStrategy":"try-catch","validationCode":"// 请求前确认 page 未被其他 goroutine 占用、处于目标 tab\nif pageBusy.Load() {\n    return errors.New(\"页面被其他请求占用，请串行化 tab 请求\")\n}","typeGuard":null,"tryCatchPattern":"res, err := client.UserProfile(ctx, userID, tab)\nif err != nil && strings.Contains(err.Error(), \"与请求的\") && strings.Contains(err.Error(), \"不符\") {\n    // tab 残留：重建 page 后重试一次\n    page = newPage()\n    res, err = client.UserProfile(ctx, userID, tab)\n}","preventionTips":["不要并发复用同一个 rod.Page 请求不同 tab","连续请求不同 tab 之间做串行化/加锁","捕获 tab 不符错误后重建页面再重试","升级库版本确认每次请求是否已做 tab 复位"],"tags":["state","tab-mismatch","scraping","concurrency"],"backgroundTag":"tab-state-mismatch","analyzedSha":"332d196854a9eac0d2b8c2c0e3d0cc43139d724c","analyzedAt":"2026-09-05T22:22:55.988Z","contentChangedAt":"2026-09-05T22:22:55.988Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}