{"record":{"id":"8ac8baf8bc35badb","repo":"fish2018/pansou","slug":"error-8ac8ba","errorCode":null,"errorMessage":"详情接口返回无效数据","messagePattern":"详情接口返回无效数据","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"plugin/nsgame/nsgame.go","lineNumber":271,"sourceCode":"func (p *NSGameAsyncPlugin) fetchDetail(client *http.Client, id int) (NSGameItem, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)\n\tdefer cancel()\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf(\"%s/game/detail?id=%d\", baseURL, id), nil)\n\tif err != nil {\n\t\treturn NSGameItem{}, err\n\t}\n\tp.setRequestHeaders(req, \"https://nsthwj.cn/\")\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn NSGameItem{}, err\n\t}\n\tdefer resp.Body.Close()\n\tvar payload NSGameDetailResponse\n\tif err := stdjson.NewDecoder(resp.Body).Decode(&payload); err != nil {\n\t\treturn NSGameItem{}, err\n\t}\n\tif !payload.Success || payload.Data.ID == 0 {\n\t\treturn NSGameItem{}, fmt.Errorf(\"详情接口返回无效数据\")\n\t}\n\treturn payload.Data, nil\n}\n\nfunc newVisitorID() string {\n\tb := make([]byte, 16)\n\tif _, err := rand.Read(b); err != nil {\n\t\treturn fmt.Sprintf(\"%x-0000-4000-8000-%012d\", md5.Sum([]byte(time.Now().String())), time.Now().UnixNano()%1e12)\n\t}\n\tb[6] = (b[6] & 0x0f) | 0x40\n\tb[8] = (b[8] & 0x3f) | 0x80\n\tbuf := make([]byte, 36)\n\thex.Encode(buf[0:8], b[0:4])\n\tbuf[8] = '-'\n\thex.Encode(buf[9:13], b[4:6])\n\tbuf[13] = '-'\n\thex.Encode(buf[14:18], b[6:8])\n\tbuf[18] = '-'","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/nsgame/nsgame.go#L253-L289","documentation":"fetchDetail decoded the detail API response but it is semantically invalid: payload.Success is false or payload.Data.ID is 0, so no usable detail record exists. The plugin returns this sentinel error; the caller (the goroutine in searchImpl) silently drops the item, so the resource is simply missing from results.","triggerScenarios":"Detail endpoint returns success=false for a deleted/private resource, the item ID doesn't exist anymore, or the session is invalid so the detail API rejects the request while still returning JSON.","commonSituations":"Search index lists entries whose detail pages were removed (dead links), resource made private, or anti-bot session expired between the search call and the detail calls.","solutions":["Log the item ID and raw payload when this fires to confirm whether it's dead content or an auth problem","Refresh the session and retry the detail fetch once before giving up","Skip and continue (current behavior) is correct for dead resources; surface a metric instead of failing the whole search","Verify the detail URL path still matches the current site API","Check if the API now nests data differently so Data.ID is always 0 due to a schema change"],"exampleFix":"// before\nif !payload.Success || payload.Data.ID == 0 {\n    return NSGameItem{}, fmt.Errorf(\"详情接口返回无效数据\")\n}\n// after\nif !payload.Success || payload.Data.ID == 0 {\n    return NSGameItem{}, fmt.Errorf(\"详情接口返回无效数据: id=%d, success=%v\", itemID, payload.Success)\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(keyword, ext)\n// this error is swallowed per-item by the plugin; instead validate the result set\nif err == nil && len(results) == 0 {\n    log.Warn(\"nsgame returned no usable detail records\")\n}","preventionTips":["Expect dead/private resources in any scraped index — drop them silently but count them","Refresh the anti-bot session if detail failures spike suddenly","Log item IDs on invalid detail payloads to distinguish dead links from auth issues","Cross-check with a second source when detail data is missing"],"tags":["api","empty-data","go","detail-fetch"],"backgroundTag":"unexpected-api-response-shape","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"}