{"record":{"id":"495afd59ee5df6b6","repo":"fish2018/pansou","slug":"getkey-id-d-code-d-msg-s","errorCode":null,"errorMessage":"getKey id=%d code=%d msg=%s","messagePattern":"getKey id=(.+?) code=(.+?) msg=(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/yingso/yingso.go","lineNumber":248,"sourceCode":"\t\treturn nil, fmt.Errorf(\"[%s] 搜索接口异常: code=%d msg=%s\", p.Name(), response.Code, response.Msg)\n\t}\n\treturn response.Data, nil\n}\n\nfunc (p *YingsoPlugin) resolveItem(ctx context.Context, client *http.Client, config apiConfig, item searchItem) (model.SearchResult, error) {\n\tpayload := getKeyPayload{ID: item.ID, UserID: config.UserID}\n\tencrypted, err := encryptPayload(payload, config)\n\tif err != nil {\n\t\treturn model.SearchResult{}, err\n\t}\n\n\tvar response apiEnvelope[string]\n\tendpoint := fmt.Sprintf(\"%s/%s/getKey\", p.apiBaseURL, url.PathEscape(config.URLVersion))\n\tif err := p.requestJSON(ctx, client, http.MethodPost, endpoint, encrypted, &response); err != nil {\n\t\treturn model.SearchResult{}, err\n\t}\n\tif response.Code != http.StatusOK || strings.TrimSpace(response.Data) == \"\" {\n\t\treturn model.SearchResult{}, fmt.Errorf(\"getKey id=%d code=%d msg=%s\", item.ID, response.Code, response.Msg)\n\t}\n\n\tlinkType, linkURL, password := buildLink(item.Root, response.Data)\n\tif linkURL == \"\" {\n\t\treturn model.SearchResult{}, fmt.Errorf(\"不支持的网盘类型 root=%d id=%d\", item.Root, item.ID)\n\t}\n\n\ttitle := cleanText(item.Title)\n\tif title == \"\" {\n\t\ttitle = fmt.Sprintf(\"影搜资源 %d\", item.ID)\n\t}\n\tid := fmt.Sprintf(\"%s-%d\", pluginName, item.ID)\n\tnow := time.Now()\n\treturn model.SearchResult{\n\t\tMessageID: id,\n\t\tUniqueID:  id,\n\t\tChannel:   \"\",\n\t\tDatetime:  now,","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/yingso/yingso.go#L230-L266","documentation":"This error is returned by YingsoPlugin.resolveItem when the /getKey endpoint returns a non-200 code in its envelope OR returns an empty/whitespace data field, meaning the share key for a specific search result could not be obtained. It embeds the item ID plus the API's code and msg so the failing resource can be identified.","triggerScenarios":"POST {apiBaseURL}/{url_version}/getKey with {id, userId} returns apiEnvelope[string] where code != 200 or strings.TrimSpace(response.Data) == \"\" — typically the resource was removed, is private, has no share key, or the encrypted payload was rejected.","commonSituations":"Search results are stale and the underlying netdisk share has been deleted or taken down; the API withholds keys for sensitive resources; heavy concurrency (up to 8 parallel getKey calls) triggers per-item rate limiting; user_id from bootstrap config became invalid.","solutions":["Log item.ID with the API code/msg to confirm whether it is per-resource (deleted/private) or global (auth/rate limit).","Re-fetch bootstrap config (url_version, user_id) via fetchConfig and retry resolveItem if code indicates auth/parameter issues.","Apply a small backoff/retry for transient codes; individual item failures are already tolerated by searchImpl unless ALL items fail.","Treat empty data with code 200 as 'resource unavailable' and skip the item rather than surfacing it as a hard failure.","If many items fail simultaneously, suspect the Yingso API changed its getKey contract and update the payload/encryption accordingly."],"exampleFix":"// before\nif response.Code != http.StatusOK || strings.TrimSpace(response.Data) == \"\" {\n\treturn model.SearchResult{}, fmt.Errorf(\"getKey id=%d code=%d msg=%s\", item.ID, response.Code, response.Msg)\n}\n// after\nif response.Code != http.StatusOK || strings.TrimSpace(response.Data) == \"\" {\n\tif isTransientCode(response.Code) {\n\t\ttime.Sleep(200 * time.Millisecond)\n\t\treturn p.resolveItem(ctx, client, config, item)\n\t}\n\treturn model.SearchResult{}, fmt.Errorf(\"getKey id=%d code=%d msg=%s\", item.ID, response.Code, response.Msg)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"result, err := resolveItem(ctx, client, config, item)\nif err != nil {\n\tif strings.Contains(err.Error(), \"getKey\") {\n\t\t// per-item failure: skip this item, continue with others\n\t\tlog.Printf(\"skipping item (%v)\", err)\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Expect per-item getKey failures (deleted/private shares) and skip them individually.","Re-fetch bootstrap config periodically so user_id/url_version stay valid.","Cap getKey concurrency and add per-call timeouts to avoid rate-limit rejections.","Treat empty data with code 200 as 'no share available' rather than retrying forever."],"tags":["http","api","upstream","resource-not-found"],"backgroundTag":"api-error-response","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"}