{"record":{"id":"4dcce21fa9d7126f","repo":"fish2018/pansou","slug":"no-posts-found","errorCode":null,"errorMessage":"no posts found","messagePattern":"no posts found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugin/discourse/discourse.go","lineNumber":500,"sourceCode":"\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\t// 读取响应体\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response failed: %w\", err)\n\t}\n\n\t// 解析JSON响应\n\tvar detailResp DetailResponse\n\tif err := json.Unmarshal(body, &detailResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse json failed: %w\", err)\n\t}\n\n\t// 提取第一个帖子的链接\n\tif len(detailResp.PostStream.Posts) == 0 {\n\t\treturn nil, fmt.Errorf(\"no posts found\")\n\t}\n\n\tmainPost := detailResp.PostStream.Posts[0]\n\t\n\t// 从 link_counts 中提取网盘链接\n\tvar links []model.Link\n\tfor _, linkCount := range mainPost.LinkCounts {\n\t\t// 跳过内部链接\n\t\tif linkCount.Internal {\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\t// 判断是否为网盘链接并解析\n\t\tlink := p.parseNetDiskLink(linkCount.URL)\n\t\tif link != nil {\n\t\t\tlinks = append(links, *link)\n\t\t}\n\t}","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/discourse/discourse.go#L482-L518","documentation":"Empty-result error in GetTopicDetail (plugin/discourse/discourse.go:500): the JSON parsed fine but PostStream.Posts is empty, so there is no post from which to extract pan links. Fires for topics that exist yet contain no posts the endpoint returned (empty or restricted topic).","triggerScenarios":"Detail JSON parsed successfully but post_stream.posts array is empty — e.g. topic is empty/deleted, access restricted so posts are withheld, or the JSON shape changed so posts land elsewhere.","commonSituations":"主题仅含标题无正文；API 返回了被审核删除后的空帖列表。","solutions":["Verify the topic actually has posts by opening it in a browser","Check whether the topic requires login; authenticate the scraper session if so","Update DetailResponse struct to match the forum's current Discourse API schema","Treat as an empty result and skip the topic instead of failing hard"],"exampleFix":"// before\nif len(detailResp.PostStream.Posts) == 0 {\n    return nil, fmt.Errorf(\"no posts found\")\n}\n// after\nif len(detailResp.PostStream.Posts) == 0 {\n    return nil, nil // empty topic; nothing to extract\n}","handlingStrategy":"fallback","validationCode":"// check the topic has content before extracting links\nresp, _ := http.Get(baseURL + \"/t/\" + id + \".json\")\nvar probe struct{ PostsCount int `json:\"posts_count\"` }\njson.NewDecoder(resp.Body).Decode(&probe)\nif probe.PostsCount == 0 {\n    return nil // skip empty topic\n}","typeGuard":"func hasPosts(d DetailResponse) bool { return len(d.PostStream.Posts) > 0 }","tryCatchPattern":"links, err := plugin.GetTopicDetail(id)\nif err != nil {\n    if err.Error() == \"no posts found\" {\n        log.Printf(\"topic %d has no posts; skipping\", id)\n        return nil, nil\n    }\n    return nil, err\n}","preventionTips":["Skip topics with zero posts_count before fetching detail","Authenticate when topics require login","Keep DetailResponse fields in sync with the forum's Discourse version"],"tags":["empty-result","discourse","api","parsing"],"backgroundTag":"empty-result-set","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}