GopeedLab/gopeed · error

blob source revoked

Error message

blob source revoked

What it means

Returned when a blob source still exists in the map but is marked revoked. Registry.get checks src.revoked (registry.go:626-631), and Source.acquireTask/releaseTask return it directly (registry.go:403-405,420-422). Revocation is how the registry retires a source: explicit Revoke, the final task Release, expiry of an unclaimed session-backed source after unclaimedSourceTTL (10 minutes), or Registry.Close.

Source

Thrown at internal/blob/registry.go:33

	"strconv"
	"strings"
	"sync"
	"time"
)

const urlPathPrefix = "/__blob/"

const rangeSourceFailureLimit = 2

// unclaimedSourceTTL bounds how long a session-backed source may keep its
// engine alive without ever being claimed by a download task.
var unclaimedSourceTTL = 10 * time.Minute

var (
	ErrInvalidURL      = errors.New("invalid blob url")
	ErrInvalidOptions  = errors.New("invalid blob options")
	ErrSourceNotFound  = errors.New("blob source not found")
	ErrSourceRevoked   = errors.New("blob source revoked")
	ErrSourceClosed    = errors.New("blob source closed")
	ErrRangeNotAllowed = errors.New("blob range not allowed")
)

type SessionRef interface {
	Retain()
	Release()
}

type OpenRequest struct {
	Offset int64
	End    int64
}

type OpenFunc func(ctx context.Context, req OpenRequest) (io.ReadCloser, error)

type CreateOptions struct {
	ContentType string

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Consider the source dead: create a new one and rebind the task's URL
  2. Acquire the source before starting the download task, not after, so the unclaimed TTL timer is cancelled (acquireTask stops it)
  3. Avoid re-using a blob URL after you have called Release for its last reference or Revoke
  4. If seen right after shutdown starts, it is benign racing with Registry.Close — skip the operation instead of retrying

Example fix

// before
_ = registry.Release(blobURL)
err := registry.Acquire(blobURL) // ErrSourceRevoked

// after
_ = registry.Release(blobURL)
blobURL, err = registry.CreateOpener(open, opts) // fresh source, fresh URL
if err != nil { return err }
err = registry.Acquire(blobURL)
Defensive patterns

Strategy: try-catch

Try / catch

if err := registry.Acquire(blobURL); err != nil {
    if errors.Is(err, blob.ErrSourceRevoked) {
        // source was revoked (Revoke/final Release/TTL/Close): recreate it
        blobURL, err = registry.CreateOpener(open, opts)
        if err != nil { return err }
    }
}

Prevention

When it happens

Trigger: Calling Acquire/Release after Revoke(blobURL); calling Release for the last task reference and then any further blob operation on the same URL; creating a session-backed source and Acquiring it more than 10 minutes after creation; any blob call racing with Registry.Close (Close marks every source revoked).

Common situations: Extension/UI code holding onto a blob URL after the download finished (final Release already revoked it); revoking a source on cancel but a queued re-try path still referencing the old URL; slow test suites exceeding the 10-minute TTL between CreateOpener and Acquire.

Related errors


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/6044ea3df79d5da5. Report an issue: GitHub.