Tencent/WeKnora · error

list docs for book %d: %w

Error message

list docs for book %d: %w

What it means

In walk, after a book ID parses successfully the connector calls cli.ListBookDocs to enumerate the book's documents. This error wraps any failure of that upstream Yuque API call, identifying which book ID failed. The cause is preserved with %w.

Source

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

) ([]types.FetchedItem, *yuqueCursor, error) {
	cfg, err := parseYuqueConfig(config)
	if err != nil {
		return nil, nil, err
	}
	cli := newClient(cfg)

	newCursor := &yuqueCursor{LastSyncTime: time.Now(), BookDocTimes: make(map[string]map[string]string)}
	var out []types.FetchedItem

	for _, bookIDStr := range resourceIDs {
		bookID, err := strconv.ParseInt(bookIDStr, 10, 64)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid book id %q: %w", bookIDStr, err)
		}

		docs, err := cli.ListBookDocs(ctx, bookID)
		if err != nil {
			return nil, nil, fmt.Errorf("list docs for book %d: %w", bookID, err)
		}

		currentDocs := make(map[string]bool)
		newCursor.BookDocTimes[bookIDStr] = make(map[string]string)

		var skippedType, skippedDraft, kept int
		var sampleSkipType, sampleSkipDraft string
		for _, d := range docs {
			// Empty type/status is treated as acceptable — forward-compat with
			// API variations that omit the field.
			if d.Type != "" && d.Type != "Doc" {
				skippedType++
				if sampleSkipType == "" {
					sampleSkipType = fmt.Sprintf("id=%d type=%q title=%q", d.ID, d.Type, d.Title)
				}
				continue
			}
			if d.Status != "" && d.Status != "1" {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Verify the book still exists and is readable with curl https://api.yuque.com/api/v2/repos/<bookID>/docs using the configured token
  2. Remove or update stale book IDs in ResourceIDs if the repo was deleted/moved
  3. Re-issue the token with read access to all configured books (or an org-wide token)
  4. Retry on transient network/5xx/429 errors; reduce poll frequency if rate limited

Example fix

// before
docs, err := cli.ListBookDocs(ctx, bookID)
if err != nil {
    return nil, nil, fmt.Errorf("list docs for book %d: %w", bookID, err)
}
// after
docs, err := cli.ListBookDocs(ctx, bookID)
if err != nil {
    return nil, nil, fmt.Errorf("list docs for book %d: %w", bookID, err)
}
// plus caller-side: validate book IDs via a permission check before configuring them
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify each configured book is readable before syncing:
// GET https://api.yuque.com/api/v2/repos/<bookID> with the token
// non-200 => remove or fix that book ID in ResourceIDs

Type guard

var apiErr *yuque.APIError
if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
    // book deleted or inaccessible: drop it from ResourceIDs and resync
}

Try / catch

items, cur, err := ds.Fetch(ctx, cfg, cursor)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || isTransient(err) {
        return retryWithBackoff(ctx)
    }
    return fmt.Errorf("incremental fetch failed: %w", err)
}

Prevention

When it happens

Trigger: ListBookDocs(ctx, bookID) errors for a configured, numerically valid book ID — book deleted or access revoked, token lacks scope on that repo, network failure, or API rate limiting during a multi-book walk.

Common situations: Book was deleted or moved to another group after being configured; token belongs to a user/organization no longer having read access to the repo; 404 from Yuque for private books the token can't see.

Related errors


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