{"record":{"id":"16b79f12eccae85b","repo":"wtfutil/wtf","slug":"error-fetching-tasks-s","errorCode":null,"errorMessage":"error fetching tasks: %s","messagePattern":"error fetching tasks: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/asana/client.go","lineNumber":28,"sourceCode":"\nfunc fetchTasksFromProject(token, projectId, mode string) ([]*TaskItem, error) {\n\ttaskItems := []*TaskItem{}\n\tuidToName := make(map[string]string)\n\n\tclient := asana.NewClientWithAccessToken(token)\n\n\tuid, err := getCurrentUserId(client, mode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tq := &asana.TaskQuery{\n\t\tProject: projectId,\n\t}\n\n\tfetchedTasks, _, err := getTasksFromAsana(client, q)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error fetching tasks: %s\", err)\n\t}\n\n\tprocessFetchedTasks(client, &fetchedTasks, &taskItems, &uidToName, mode, projectId, uid)\n\n\treturn taskItems, nil\n}\n\nfunc fetchTasksFromProjectSections(token, projectId string, sections []string, mode string) ([]*TaskItem, error) {\n\ttaskItems := []*TaskItem{}\n\tuidToName := make(map[string]string)\n\n\tclient := asana.NewClientWithAccessToken(token)\n\n\tuid, err := getCurrentUserId(client, mode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/wtfutil/wtf/blob/bb838c1ccb0f0f3223690df44afdec663d622881/modules/asana/client.go#L10-L46","documentation":"The Asana module's fetchTasksFromProject calls getTasksFromAsana (the go-asana client's task query). If that API call returns an error, it is wrapped with fmt.Errorf(\"error fetching tasks: %s\", err), preserving the underlying cause (auth failure, bad project ID, network error) while labeling it as a task-fetch failure.","triggerScenarios":"Fetch -> fetchTasksFromProject: getTasksFromAsana(client, &asana.TaskQuery{Project: projectId}) returns a non-nil error — invalid or expired Asana personal access token, nonexistent/inaccessible project ID, network failure, or Asana API rate limiting.","commonSituations":"Wrong or revoked Asana PAT in module settings; projectId typo'd or the token's user lacks access to the workspace/project; offline/laptop asleep; hitting Asana API rate limits.","solutions":["Read the wrapped %s cause in the error text to identify the root failure.","Verify the Asana personal access token is valid and has access to the target project/workspace.","Confirm the projectId in the module settings matches a real project the token can read.","Check network connectivity to app.asana.com/api and retry; if rate-limited, back off and reduce poll frequency."],"exampleFix":"// before: error fetching tasks: asana: not found (wrong projectId)\nasana:\n  apiKey: \"1/1234:abcdef\"\n  project: \"9999999999999\"  # nonexistent\n\n// after\nasana:\n  apiKey: \"1/1234:abcdef\"   # valid PAT\n  project: \"1201234567890123\"  # correct project GID","handlingStrategy":"retry","validationCode":"if apiKey == \"\" || projectID == \"\" {\n    return errors.New(\"asana apiKey and projectId must be configured\")\n}","typeGuard":null,"tryCatchPattern":"tasks, err := fetchTasksFromProject(client, projectId, mode, uid)\nif err != nil {\n    var asanaErr *asana.ErrorResponse\n    if errors.As(err, &asanaErr) {\n        log.Printf(\"asana API error: %v\", asanaErr)\n    }\n    // retry with backoff for transient/network errors\n}","preventionTips":["Verify the Asana PAT and project GID with a curl call before configuring.","Ensure the token's user has access to the project's workspace.","Add exponential backoff for rate limits and transient network failures."],"tags":["asana","api","wrapped-error","configuration"],"backgroundTag":"api-error-wrapped","analyzedSha":"bb838c1ccb0f0f3223690df44afdec663d622881","analyzedAt":"2026-09-03T17:02:45.030Z","contentChangedAt":"2026-09-03T17:02:45.030Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}