{"record":{"id":"2c1b608b865b8971","repo":"Tencent/WeKnora","slug":"get-current-user-w","errorCode":null,"errorMessage":"get current user: %w","messagePattern":"get current user: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/yuque/connector.go","lineNumber":70,"sourceCode":"// Serial fetch for v1 (user groups typically <10). TODO(perf): parallelize if slow.\nfunc (c *Connector) ListResources(\n\tctx context.Context, config *types.DataSourceConfig, parentID string,\n) ([]types.Resource, error) {\n\t// Yuque resources are a flat list of repositories (no nesting), so a\n\t// lazy-load request for a specific parent has nothing extra to return.\n\tif parentID != \"\" {\n\t\treturn []types.Resource{}, nil\n\t}\n\n\tcfg, err := parseYuqueConfig(config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcli := newClient(cfg)\n\n\tme, err := cli.GetCurrentUser(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"get current user: %w\", err)\n\t}\n\n\trepos := make(map[int64]v2Repo)\n\n\t// Team token: /api/v2/user returns type=\"Group\" — the token represents a team,\n\t// not a personal user. In this case, directly list the team's own repos instead\n\t// of going through the personal-repos + user-groups flow.\n\tif me.Type == \"Group\" {\n\t\tlogger.Infof(ctx, \"[Yuque] detected team token (type=Group, login=%s), listing team repos directly\", me.Login)\n\t\tteamRepos, err := cli.ListGroupRepos(ctx, me.Login)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"list team repos: %w\", err)\n\t\t}\n\t\tfor _, r := range teamRepos {\n\t\t\trepos[r.ID] = r\n\t\t}\n\t} else {\n\t\t// Personal token flow: list user's own repos + repos from joined groups.","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/yuque/connector.go#L52-L88","documentation":"Connector.ListResources calls GetCurrentUser (GET /api/v2/user) to identify whether the token is personal or a team token; any failure there is wrapped as \"get current user: %w\". Since virtually every subsequent step depends on knowing the user, this fails the whole listing. The wrapped cause can be transport errors, 401/403 ErrInvalidCredentials, 429, 4xx/5xx API errors, or JSON decode failures.","triggerScenarios":"ListResources(ctx, config, \"\") invoked during resource browsing or sync when GET /api/v2/user fails: expired/revoked token (401), token lacking permission (403), network failure/timeout, rate limit exhausted after retries, or an unexpected 200 payload that fails to decode into v2UserResponse.","commonSituations":"Token invalidated after a password change or organization removal; a previously-valid token expired mid-session so browsing the source suddenly fails; rate limiting after heavy sync activity; transient network blips between the deployment and Yuque; self-hosted instance momentarily down.","solutions":["Check errors.Is(err, datasource.ErrInvalidCredentials) first — if true, re-authorize: generate a fresh token in Yuque settings and update the datasource.","For rate-limit messages, wait for the window to reset (Yuque allows roughly 100 req/5min on personal tokens) and reduce polling frequency.","For timeouts/transport errors, verify egress connectivity from the deployment to the configured baseURL (DNS, proxy, firewall) and retry.","For 5xx or decode errors, check the Yuque instance health/status page and retry; if persistent on self-hosted, upgrade or inspect the instance.","Re-run the source's Validate to confirm credentials work before debugging ListResources further."],"exampleFix":"resources, err := connector.ListResources(ctx, cfg, \"\")\nif err != nil {\n    if errors.Is(err, datasource.ErrInvalidCredentials) {\n        return nil, fmt.Errorf(\"yuque token invalid, please reconnect the source: %w\", err)\n    }\n    return nil, fmt.Errorf(\"listing yuque books failed (transient?): %w\", err) // safe to retry\n}","handlingStrategy":"retry","validationCode":"// Go: ensure the datasource is valid before listing resources\nif err := connector.Validate(ctx, cfg); err != nil {\n    return nil, fmt.Errorf(\"fix credentials before listing resources: %w\", err)\n}","typeGuard":"// Go: check whether the current-user failure is credential-related\nfunc isAuthError(err error) bool {\n    return errors.Is(err, datasource.ErrInvalidCredentials)\n}","tryCatchPattern":"var resources []types.Resource\nerr := retry.OnError(3, backoff.Exponential(2*time.Second), func() error {\n    var e error\n    resources, e = connector.ListResources(ctx, cfg, \"\")\n    if e != nil && isAuthError(e) {\n        return backoff.Permanent(e) // do not retry bad credentials\n    }\n    return e // retry transient failures\n})\nif err != nil {\n    return fmt.Errorf(\"list yuque resources: %w\", err)\n}","preventionTips":["Retry transient failures (5xx, timeouts, rate limits) with backoff, but never retry ErrInvalidCredentials.","Throttle GetDocDetail-style call rates (~300ms pause) to stay under Yuque's rate limits so /user calls aren't throttled either.","Cache the current-user/repo listing briefly so repeated UI browses don't hammer the API.","Alert on auth errors so expired/revoked tokens are re-issued before sync jobs fail.","Call Validate before long-running jobs so credential problems surface up front, not mid-listing."],"tags":["yuque","current-user","credentials","resource-listing"],"backgroundTag":"connection-validation-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}