{"record":{"id":"79af0529a1ea2545","repo":"goharbor/harbor","slug":"not-found-79af05","errorCode":"NOT_FOUND","errorMessage":"webhook task %d not found","messagePattern":"webhook task (.+?) not found","errorType":"error_code","errorClass":"github.com/goharbor/harbor/src/lib/errors.Error","httpStatus":404,"severity":"error","filePath":"src/controller/webhook/controller.go","lineNumber":148,"sourceCode":"\treturn c.taskMgr.Count(ctx, buildTaskQuery(execID, query))\n}\n\nfunc (c *controller) ListTasks(ctx context.Context, execID int64, query *q.Query) ([]*task.Task, error) {\n\treturn c.taskMgr.List(ctx, buildTaskQuery(execID, query))\n}\n\nfunc (c *controller) GetTask(ctx context.Context, taskID int64) (*task.Task, error) {\n\tquery := q.New(q.KeyWords{\n\t\t\"id\":          taskID,\n\t\t\"vendor_type\": webhookJobVendors,\n\t})\n\ttasks, err := c.taskMgr.List(ctx, query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(tasks) == 0 {\n\t\treturn nil, errors.New(nil).WithCode(errors.NotFoundCode).\n\t\t\tWithMessagef(\"webhook task %d not found\", taskID)\n\t}\n\treturn tasks[0], nil\n}\n\nfunc (c *controller) GetTaskLog(ctx context.Context, taskID int64) ([]byte, error) {\n\t// ensure the webhook task exist\n\t_, err := c.GetTask(ctx, taskID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c.taskMgr.GetLog(ctx, taskID)\n}\n\nfunc buildExecutionQuery(policyID int64, query *q.Query) *q.Query {\n\tquery = q.MustClone(query)\n\tquery.Keywords[\"vendor_type\"] = webhookJobVendors","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/src/controller/webhook/controller.go#L130-L166","documentation":"The webhook controller's GetTask (src/controller/webhook/controller.go:148) lists tasks by ID filtered to webhook vendor types; zero matches returns NOT_FOUND (HTTP 404). GetTaskLog raises the same error because it first verifies the task exists and belongs to a webhook.","triggerScenarios":"GET webhook task or task-log endpoints with a task ID that doesn't exist, belongs to another job vendor type (replication, scan, GC), or was cleaned up after its retention window.","commonSituations":"Dashboards caching task IDs from old webhook executions; passing the webhook execution ID instead of the task ID; task records pruned by jobservice log cleanup.","solutions":["List tasks for the execution first and use an ID from that list","Confirm the ID is a webhook task ID, not an execution ID or a replication task ID","Treat 404 as terminal - the task log is gone; enable webhook debug logging earlier next time"],"exampleFix":"// before\nlog, err := webhookCtl.GetTaskLog(ctx, 888)  // 404\n\n// after\ntasks, _ := webhookCtl.ListTasks(ctx, q.New(q.KeyWords{\"ExecutionID\": execID}))\nfor _, t := range tasks {\n    log, err := webhookCtl.GetTaskLog(ctx, t.ID)\n    ...\n}","handlingStrategy":"validation","validationCode":"tasks, err := webhookCtl.ListTasks(ctx, q.New(q.KeyWords{\\\"ExecutionID\\\": execID}))\nif err != nil { return err }\nfor _, t := range tasks {\n    if t.ID == taskID {\n        // safe to fetch log\n    }\n}","typeGuard":"func isWebhookTaskNotFound(err error) bool {\n    return errors.IsNotFoundErr(err) || errors.IsErr(err, errors.NotFoundCode)\n}","tryCatchPattern":"log, err := webhookCtl.GetTaskLog(ctx, taskID)\nif err != nil {\n    if errors.IsNotFoundErr(err) {\n        // task of another vendor type or pruned; stop and report\n    }\n    return err\n}","preventionTips":["Only use task IDs returned by the webhook execution's task list","Do not reuse IDs from other job vendors (replication/scan)","Handle 404 as permanent; cache logs you need beyond the retention window"],"tags":["harbor","webhook","task","not-found","api"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}