Tencent/WeKnora · error

list personal repos: %w

Error message

list personal repos: %w

What it means

This error wraps a failure from cli.ListUserRepos during ListResources' personal-token flow. The /me response indicated a personal user, so the connector fetches the user's own repos; any API-level failure there is wrapped with this prefix while preserving the cause with %w.

Source

Thrown at internal/datasource/connector/yuque/connector.go:91

	repos := make(map[int64]v2Repo)

	// Team token: /api/v2/user returns type="Group" — the token represents a team,
	// not a personal user. In this case, directly list the team's own repos instead
	// of going through the personal-repos + user-groups flow.
	if me.Type == "Group" {
		logger.Infof(ctx, "[Yuque] detected team token (type=Group, login=%s), listing team repos directly", me.Login)
		teamRepos, err := cli.ListGroupRepos(ctx, me.Login)
		if err != nil {
			return nil, fmt.Errorf("list team repos: %w", err)
		}
		for _, r := range teamRepos {
			repos[r.ID] = r
		}
	} else {
		// Personal token flow: list user's own repos + repos from joined groups.
		personal, err := cli.ListUserRepos(ctx, me.Login)
		if err != nil {
			return nil, fmt.Errorf("list personal repos: %w", err)
		}
		for _, r := range personal {
			if _, ok := repos[r.ID]; !ok {
				repos[r.ID] = r
			}
		}

		groups, err := cli.ListUserGroups(ctx, me.ID)
		if err != nil {
			// Yuque returns 404 when the user has not joined any groups (teams),
			// instead of an empty list. Treat this as "no groups" and continue
			// — personal repos were already fetched above.
			logger.Warnf(ctx, "[Yuque] list user groups failed (treating as empty): %v", err)
			groups = nil
		}
		for _, g := range groups {
			teamRepos, err := cli.ListGroupRepos(ctx, g.Login)
			if err != nil {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Verify the personal token with curl https://api.yuque.com/api/v2/user and regenerate it if it returns 401
  2. Ensure the token has read scope for the user's repositories (read_repo or broader)
  3. Inspect the wrapped error: 429 means back off/poll less frequently; 5xx means retry later or check Yuque status
  4. Confirm the connector host can reach api.yuque.com (proxy/DNS issues)

Example fix

// before
personal, err := cli.ListUserRepos(ctx, me.Login)
if err != nil {
    return nil, fmt.Errorf("list personal repos: %w", err)
}
// after
personal, err := cli.ListUserRepos(ctx, me.Login)
if err != nil {
    return nil, fmt.Errorf("list personal repos (user=%s): %w", me.Login, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe the personal token before configuring:
// GET https://api.yuque.com/api/v2/user with X-Auth-Token
// treat 401 as invalid token, 403 as missing scope

Type guard

var apiErr *yuque.APIError
if errors.As(err, &apiErr) {
    switch apiErr.StatusCode {
    case 401: // re-auth
    case 429: // backoff
    }
}

Try / catch

items, err := ds.ListResources(ctx, cfg)
if err != nil {
    var apiErr *yuque.APIError
    if errors.As(err, &apiErr) && apiErr.StatusCode == 429 {
        time.Sleep(backoff)
        items, err = ds.ListResources(ctx, cfg)
    }
    if err != nil {
        log.Printf("personal repo listing failed: %v", err)
    }
}

Prevention

When it happens

Trigger: A personal Yuque token is configured and ListUserRepos(me.Login) returns an error — invalid/expired token, network failure, rate limiting, or a user whose repos endpoint returns an unexpected error.

Common situations: Personal access token expired or revoked; token created without repo read scope; user account deactivated; yuque API outage or 429 rate limit under heavy polling.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/dea2fc0cf536b149. Report an issue: GitHub.