{"record":{"id":"7664501eb67b2ed5","repo":"Tencent/WeKnora","slug":"list-docs-for-book-d-w","errorCode":null,"errorMessage":"list docs for book %d: %w","messagePattern":"list docs for book (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/yuque/connector.go","lineNumber":174,"sourceCode":") ([]types.FetchedItem, *yuqueCursor, error) {\n\tcfg, err := parseYuqueConfig(config)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tcli := newClient(cfg)\n\n\tnewCursor := &yuqueCursor{LastSyncTime: time.Now(), BookDocTimes: make(map[string]map[string]string)}\n\tvar out []types.FetchedItem\n\n\tfor _, bookIDStr := range resourceIDs {\n\t\tbookID, err := strconv.ParseInt(bookIDStr, 10, 64)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"invalid book id %q: %w\", bookIDStr, err)\n\t\t}\n\n\t\tdocs, err := cli.ListBookDocs(ctx, bookID)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"list docs for book %d: %w\", bookID, err)\n\t\t}\n\n\t\tcurrentDocs := make(map[string]bool)\n\t\tnewCursor.BookDocTimes[bookIDStr] = make(map[string]string)\n\n\t\tvar skippedType, skippedDraft, kept int\n\t\tvar sampleSkipType, sampleSkipDraft string\n\t\tfor _, d := range docs {\n\t\t\t// Empty type/status is treated as acceptable — forward-compat with\n\t\t\t// API variations that omit the field.\n\t\t\tif d.Type != \"\" && d.Type != \"Doc\" {\n\t\t\t\tskippedType++\n\t\t\t\tif sampleSkipType == \"\" {\n\t\t\t\t\tsampleSkipType = fmt.Sprintf(\"id=%d type=%q title=%q\", d.ID, d.Type, d.Title)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif d.Status != \"\" && d.Status != \"1\" {","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/yuque/connector.go#L156-L192","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the book still exists and is readable with curl https://api.yuque.com/api/v2/repos/<bookID>/docs using the configured token","Remove or update stale book IDs in ResourceIDs if the repo was deleted/moved","Re-issue the token with read access to all configured books (or an org-wide token)","Retry on transient network/5xx/429 errors; reduce poll frequency if rate limited"],"exampleFix":"// before\ndocs, err := cli.ListBookDocs(ctx, bookID)\nif err != nil {\n    return nil, nil, fmt.Errorf(\"list docs for book %d: %w\", bookID, err)\n}\n// after\ndocs, err := cli.ListBookDocs(ctx, bookID)\nif err != nil {\n    return nil, nil, fmt.Errorf(\"list docs for book %d: %w\", bookID, err)\n}\n// plus caller-side: validate book IDs via a permission check before configuring them","handlingStrategy":"try-catch","validationCode":"// Verify each configured book is readable before syncing:\n// GET https://api.yuque.com/api/v2/repos/<bookID> with the token\n// non-200 => remove or fix that book ID in ResourceIDs","typeGuard":"var apiErr *yuque.APIError\nif errors.As(err, &apiErr) && apiErr.StatusCode == 404 {\n    // book deleted or inaccessible: drop it from ResourceIDs and resync\n}","tryCatchPattern":"items, cur, err := ds.Fetch(ctx, cfg, cursor)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) || isTransient(err) {\n        return retryWithBackoff(ctx)\n    }\n    return fmt.Errorf(\"incremental fetch failed: %w\", err)\n}","preventionTips":["Periodically re-verify configured book IDs still exist and are readable","Use a token with access to all configured books (org-wide preferred)","Handle 404 by flagging stale book IDs instead of failing the whole sync","Back off on 429/5xx and cap concurrent book fetches"],"tags":["yuque","api-error","permissions","connector"],"backgroundTag":"upstream-api-call-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}