{"record":{"id":"70a99afc68284be9","repo":"xpzouying/xiaohongshu-mcp","slug":"error-70a99a","errorCode":null,"errorMessage":"标题长度超过限制","messagePattern":"标题长度超过限制","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"service.go","lineNumber":208,"sourceCode":"\t\tif loginAction.WaitForLogin(ctxTimeout) {\n\t\t\tif err := saveCookies(page); err != nil {\n\t\t\t\tlogrus.Errorf(\"扫码成功但保存 cookies 失败，会话 #%d: %v\", seq, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlogrus.Infof(\"扫码登录成功，cookies 已保存，会话 #%d\", seq)\n\t\t\treturn\n\t\t}\n\n\t\t// 没等到扫码：要么超时，要么被新取的二维码取代\n\t\tlogrus.Infof(\"登录会话 #%d 结束，未检测到扫码（超时或已被新的二维码取代）\", seq)\n\t}()\n}\n\n// PublishContent 发布内容\nfunc (s *XiaohongshuService) PublishContent(ctx context.Context, req *PublishRequest) (*PublishResponse, error) {\n\t// 验证标题长度（小红书限制：最大20个字）\n\tif xhsutil.CalcTitleLength(req.Title) > 20 {\n\t\treturn nil, fmt.Errorf(\"标题长度超过限制\")\n\t}\n\n\timagePaths, err := s.processImages(req.Images)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar scheduleTime *time.Time\n\tif req.ScheduleAt != \"\" {\n\t\tt, err := time.Parse(time.RFC3339, req.ScheduleAt)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"定时发布时间格式错误，请使用 ISO8601 格式: %v\", err)\n\t\t}\n\n\t\t// 校验定时发布时间范围：1小时至14天\n\t\tnow := time.Now()\n\t\tminTime := now.Add(1 * time.Hour)\n\t\tmaxTime := now.Add(14 * 24 * time.Hour)","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/xpzouying/xiaohongshu-mcp/blob/332d196854a9eac0d2b8c2c0e3d0cc43139d724c/service.go#L190-L226","documentation":"PublishContent validates the title against Xiaohongshu's limit of 20 characters (counted by xhsutil.CalcTitleLength, which weights CJK vs ASCII characters). Titles longer than this are rejected locally before any API call. The error is a client-side validation guard, not an upstream API error.","triggerScenarios":"Calling PublishContent (or its handlers publishHandler / handlePublishContent) with req.Title whose CalcTitleLength exceeds 20.","commonSituations":"Long descriptive titles pasted from drafts; counting raw bytes/runes instead of the platform's weighted character count; emoji or mixed CJK/Latin text pushing length over the limit.","solutions":["Shorten the title so CalcTitleLength(title) <= 20 before calling PublishContent","Pre-validate with xhsutil.CalcTitleLength and return a friendly message to the end user","Truncate the title programmatically, being careful not to cut multi-byte characters mid-point"],"exampleFix":"// before\nif len(req.Title) > 20 { ... } // byte count, wrong metric\n// after\nif xhsutil.CalcTitleLength(req.Title) > 20 {\n    return nil, fmt.Errorf(\"标题长度超过限制（最多20字）\")\n}","handlingStrategy":"validation","validationCode":"if xhsutil.CalcTitleLength(req.Title) > 20 {\n    return errors.New(\"标题最多20字\")\n}","typeGuard":"func isValidXhsTitle(title string) bool {\n    return title != \"\" && xhsutil.CalcTitleLength(title) <= 20\n}","tryCatchPattern":"resp, err := svc.PublishContent(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"标题长度超过限制\") {\n    return fmt.Errorf(\"please shorten the title to 20 characters or fewer: %w\", err)\n}","preventionTips":["Always validate titles with CalcTitleLength, never len() or utf8.RuneCountInString","Add a maxlength=20 (weighted) counter in the authoring UI","Test with emoji and mixed CJK/Latin strings to confirm the weighted count"],"tags":["go","validation","xiaohongshu","title-length"],"backgroundTag":"title-too-long","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"}