{"record":{"id":"f6743e1c7a7f0be4","repo":"GopeedLab/gopeed","slug":"blob-source-not-found","errorCode":null,"errorMessage":"blob source not found","messagePattern":"blob source not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/blob/registry.go","lineNumber":32,"sourceCode":"\t\"path\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst urlPathPrefix = \"/__blob/\"\n\nconst rangeSourceFailureLimit = 2\n\n// unclaimedSourceTTL bounds how long a session-backed source may keep its\n// engine alive without ever being claimed by a download task.\nvar unclaimedSourceTTL = 10 * time.Minute\n\nvar (\n\tErrInvalidURL      = errors.New(\"invalid blob url\")\n\tErrInvalidOptions  = errors.New(\"invalid blob options\")\n\tErrSourceNotFound  = errors.New(\"blob source not found\")\n\tErrSourceRevoked   = errors.New(\"blob source revoked\")\n\tErrSourceClosed    = errors.New(\"blob source closed\")\n\tErrRangeNotAllowed = errors.New(\"blob range not allowed\")\n)\n\ntype SessionRef interface {\n\tRetain()\n\tRelease()\n}\n\ntype OpenRequest struct {\n\tOffset int64\n\tEnd    int64\n}\n\ntype OpenFunc func(ctx context.Context, req OpenRequest) (io.ReadCloser, error)\n\ntype CreateOptions struct {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/internal/blob/registry.go#L14-L50","documentation":"Sentinel returned by Registry.getByID (registry.go:638-642) when the id parsed out of a blob URL has no entry in the sources map. The URL is well-formed for this registry, but the source it names no longer (or never did) exist. Note CreateOpener on a nil Registry also returns this error (registry.go:133-135).","triggerScenarios":"Calling Acquire/Release/Metadata/Revoke after the source was already deleted: the final Release deletes the source (registry.go:229), Revoke removes it, Registry.Close clears all sources, or a session-backed source expired via the 10-minute unclaimedSourceTTL (registry.go:27,177-185). Also using an rrId/blob URL against a fresh Registry after an app restart, or calling CreateOpener on a nil (*Registry)(nil) receiver.","commonSituations":"Re-resolving or resuming a task after process restart with a blob URL captured from the previous run; double-Release (first Release succeeds and deletes, second gets NotFound); delaying task creation more than 10 minutes after a session-backed source was created; racing with Registry.Close during shutdown.","solutions":["Treat this error as terminal for the source: re-create the source (CreateOpener/CreateBlob) and re-create/patch the task with the new URL","Call Downloader-level create promptly after creating the source so it gets Acquired before the unclaimed TTL fires","Pair every Acquire with exactly one Release and remember the first final Release deletes the source","On shutdown, stop issuing blob operations before calling Registry.Close"],"exampleFix":"// before\nif err := registry.Acquire(blobURL); err != nil { log.Fatal(err) } // crashes after restart\n\n// after\nif err := registry.Acquire(blobURL); err != nil {\n    if errors.Is(err, blob.ErrSourceNotFound) {\n        blobURL, err = registry.CreateOpener(open, opts) // recreate and rebind\n        if err != nil { return err }\n    } else {\n        return err\n    }\n}","handlingStrategy":"try-catch","validationCode":"if !registry.IsURL(blobURL) { /* will be ErrInvalidURL, not NotFound */ }\n// Nothing can prove existence cheaper than the call itself; just handle the error.","typeGuard":null,"tryCatchPattern":"if err := registry.Acquire(blobURL); err != nil {\n    if errors.Is(err, blob.ErrSourceNotFound) {\n        // terminal: recreate source and rebind task URL\n        blobURL, err = registry.CreateOpener(open, opts)\n        if err != nil { return err }\n        err = registry.Acquire(blobURL)\n    }\n    if err != nil { return err }\n}","preventionTips":["Acquire immediately after creating the source (before the 10-minute unclaimed TTL can fire)","Remember the final Release deletes the source — never use the URL afterwards","Don't expect blob sources to survive Registry.Close or process restart"],"tags":["blob","lifecycle","not-found","registry"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}