{"record":{"id":"6160db4b49bd6581","repo":"chenhg5/cc-connect","slug":"s-resource-download-auth-w","errorCode":null,"errorMessage":"%s: resource download auth: %w","messagePattern":"(.+?): resource download auth: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/feishu/resource_download.go","lineNumber":96,"sourceCode":"\t}\n\tif p.resourceDownloadHTTP == nil {\n\t\t// Defensive: callers running outside the normal constructor (notably\n\t\t// unit tests that synthesise a Platform value) still get a sane\n\t\t// client. We log instead of panicking so one stale test fixture\n\t\t// doesn't crash the whole process.\n\t\tslog.Warn(p.tag() + \": resourceDownloadHTTP is nil; using default client\")\n\t\tp.resourceDownloadHTTP = &http.Client{Timeout: 60 * time.Second}\n\t}\n\tif p.resourceChunkSize <= 0 {\n\t\tp.resourceChunkSize = defaultResourceChunkSize()\n\t}\n\tif p.resourceMaxBytes <= 0 {\n\t\tp.resourceMaxBytes = defaultResourceMaxBytes\n\t}\n\n\ttoken, err := p.fetchResourceTokenOrDefault(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: resource download auth: %w\", p.tag(), err)\n\t}\n\n\treturn p.resourceDownloadStream(ctx, token, messageID, fileKey, resType)\n}\n\n// resourceDownloadStream executes the actual download. Split out so the\n// helper's preflight (validation, token, defaults) stays readable.\nfunc (p *Platform) resourceDownloadStream(ctx context.Context, token, messageID, fileKey, resType string) ([]byte, error) {\n\tprobeCtx, cancel := context.WithTimeout(ctx, resourceRangeProbeTimeout)\n\tdefer cancel()\n\n\tfirst, total, err := p.resourceFetchFirstChunk(probeCtx, token, messageID, fileKey, resType)\n\tif err != nil {\n\t\t// Fallback: try a single plain GET. Some servers reject Range entirely\n\t\t// with 4xx instead of silently ignoring it.\n\t\tslog.Warn(p.tag()+\": first-chunk fetch failed; trying plain GET\",\n\t\t\t\"error\", err, \"file_key\", fileKey, \"type\", resType)\n\t\treturn p.resourceSingleGet(ctx, token, messageID, fileKey, resType)","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/feishu/resource_download.go#L78-L114","documentation":"This error wraps any failure to obtain the tenant access token needed to authenticate a Feishu message-resource download. downloadResourceChunked calls fetchResourceTokenOrDefault (which mints a fresh tenant_access_token via the Lark SDK) before any HTTP download; if that fails, the error is wrapped with the platform tag and 'resource download auth' context. The underlying cause (network, bad app credentials, Feishu API error) is in the wrapped error.","triggerScenarios":"Downloading an image or file from a Feishu message when fetchFreshTenantAccessToken fails: invalid app_id/app_secret, expired or revoked credentials, no network access to Feishu's auth endpoint, or a configured fetchResourceToken stub returning an error.","commonSituations":"Misconfigured app credentials in config.toml, Feishu/Lark API outage or rate limiting on the token endpoint, firewall blocking open.feishu.cn, or an app that lost its permissions after a workspace change.","solutions":["Check app_id/app_secret in config.toml and verify the app is enabled with im:resource permissions","Read the wrapped error (%w) for the real cause; if it is 99991663/99991661 the token/secret is wrong","Verify network connectivity to the Feishu (or Lark) API domain configured for the platform","Retry after a rate-limit backoff if the cause is token-endpoint throttling"],"exampleFix":"// before: hard to tell which credential failed\nsecret := os.Getenv(\"FEISHU_SECRET\")\n// after: fail fast with validation at startup\nif os.Getenv(\"FEISHU_SECRET\") == \"\" { log.Fatal(\"FEISHU_SECRET not set\") }","handlingStrategy":"try-catch","validationCode":"if appID == \"\" || appSecret == \"\" { return errors.New(\"feishu app_id/app_secret not configured\") }","typeGuard":"var authErr *AuthError; if errors.As(err, &authErr) { /* handle token failure */ }","tryCatchPattern":"data, err := agent.DownloadResource(ctx, msgID, fileKey, \"image\")\nif err != nil && strings.Contains(err.Error(), \"resource download auth\") {\n    slog.Error(\"feishu token failure; check app credentials\", \"err\", err)\n    return\n}","preventionTips":["Validate app_id/app_secret at startup with a token mint smoke test","Grant im:resource (im:message) scopes to the Feishu app","Monitor token-endpoint error rates; alert on repeated 99991663/99991661","Keep bot credentials in a secret store, rotated before expiry"],"tags":["go","feishu","authentication","network"],"backgroundTag":"authentication-required","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}