{"record":{"id":"57dcaf4437c333d9","repo":"navidrome/navidrome","slug":"getting-task-info-w","errorCode":null,"errorMessage":"getting task info: %w","messagePattern":"getting task info: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/host_taskqueue.go","lineNumber":303,"sourceCode":"\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"enqueuing task: %w\", err)\n\t}\n\n\tqs.notifyWorkers()\n\tlog.Trace(ctx, \"Enqueued task\", \"plugin\", s.pluginName, \"queue\", queueName, \"taskID\", taskID)\n\treturn taskID, nil\n}\n\n// Get returns the current state of a task.\nfunc (s *taskQueueServiceImpl) Get(ctx context.Context, taskID string) (*host.TaskInfo, error) {\n\tvar info host.TaskInfo\n\terr := s.db.QueryRowContext(ctx, `SELECT status, message, attempt FROM tasks WHERE id = ?`, taskID).\n\t\tScan(&info.Status, &info.Message, &info.Attempt)\n\tif errors.Is(err, sql.ErrNoRows) {\n\t\treturn nil, fmt.Errorf(\"task %q not found\", taskID)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"getting task info: %w\", err)\n\t}\n\treturn &info, nil\n}\n\n// Cancel cancels a pending task.\nfunc (s *taskQueueServiceImpl) Cancel(ctx context.Context, taskID string) error {\n\tnow := time.Now().UnixMilli()\n\tresult, err := s.db.ExecContext(ctx, `\n\t\tUPDATE tasks SET status = ?, updated_at = ? WHERE id = ? AND status = ?\n\t`, taskStatusCancelled, now, taskID, taskStatusPending)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"cancelling task: %w\", err)\n\t}\n\n\trowsAffected, err := result.RowsAffected()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"checking cancel result: %w\", err)\n\t}","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/navidrome/navidrome/blob/4ed7494a3293a9e9e647897ebfb9be327efd981b/plugins/host_taskqueue.go#L285-L321","documentation":"Get wraps any non-ErrNoRows error from the tasks-table SELECT with 'getting task info: %w'. Unlike the not-found case, this signals a database-level failure while reading status, message, and attempt for the given task ID.","triggerScenarios":"QueryRowContext fails for reasons other than no rows: the SQLite database is locked/corrupt, the context was cancelled mid-query, the tasks table schema is incompatible (missing columns after a version change), or the DB file is unreadable.","commonSituations":"A second process holding a write lock on the DB; corrupt database from a hard crash; querying a data directory created by an older plugin version whose tasks table lacks new columns; caller cancelling the context during polling loops.","solutions":["Read the wrapped inner error to pinpoint the cause (I/O error, lock, schema, context).","Confirm the data directory is accessible and not opened by another process.","If the schema changed between versions, back up and remove/rename the old database file so it is recreated.","Retry with a fresh or longer-lived context if the error is context cancellation or a transient lock."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"info, err := q.Get(ctx, taskID)\nif err != nil {\n    if errors.Is(err, context.Canceled) || ctx.Err() != nil {\n        return retryGet(taskID)\n    }\n    if strings.Contains(err.Error(), \"not found\") {\n        return nil // not-found is a separate, expected path\n    }\n    return fmt.Errorf(\"DB read failed: %w\", err)\n}","preventionTips":["Use long-enough context timeouts for polling loops","Keep the DB file free of competing processes","Recreate stale databases after plugin version upgrades"],"tags":["task-queue","database","sqlite"],"backgroundTag":"database-query-failed","analyzedSha":"4ed7494a3293a9e9e647897ebfb9be327efd981b","analyzedAt":"2026-09-01T05:03:05.018Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}