Use when cached_video_thumbnail misbehaves - ThumbnailException errors (timeout, network, unsupportedMedia, extractionFailed), thumbnails failing instantly on retry, wrong or stale images, cache misses or repeated extraction, scroll jank blamed on thumbnails, black or rotated frames, HLS streams, or Android/iOS behavior differences.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cached-video-thumbnail:diagnose-thumbnail-issuesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Most "bugs" reported against this engine are documented, deliberate
Most "bugs" reported against this engine are documented, deliberate behavior (negative caching, keyframe snapping, platform quirks). Diagnose from evidence - the error code and the metrics snapshot - before proposing fixes. Full tables: error-codes-and-platform-behavior.md.
ThumbnailErrorCode (from the exception, engine.onEvent
log, or metrics.failures). Never diagnose from the message string
alone; the code is the classification.ThumbnailEngine.instance.metrics and compute:
cacheHits / requests (warm scrolling should approach 1);coalescedJoins high = duplicate simultaneous requests (usually fine);extractP95 high = decode cost (spec too large? exact seeks? remote
non-faststart files?);queueWaitP95 high with low extractP95 = concurrency starvation or
a request storm;failures map = what is actually failing, by code.getThumbnail) and check the package issue tracker.| Report | Mechanism | Remedy |
|---|---|---|
| "Failed once, now fails instantly for ~a minute" | Negative cache memoizes non-cancellation failures for negativeCacheTtl (default 60s) and replays the original exception | For explicit retry buttons / connectivity-restored: await engine.evict(source) then re-request. Globally: shorter negativeCacheTtl in configure (re-running configure also clears all failure memos) |
"404 URL reports timeout not network on Android" | Some Android media stacks retry internally and never surface HTTP status; the Dart deadline ends the attempt | Documented platform quirk. Bucket timeout+network together for remote-source analytics; optionally probe the URL app-side to reclassify |
| "Cache stopped working / thumbnails regenerate after days" | Cache lives in the OS-purgeable app cache dir; the OS reclaimed it | By design; engine re-extracts on demand. Do not move the cache; reduce churn with correct shared specs |
| "Changed request headers but same thumbnail" | Headers are deliberately excluded from cache identity | evict(source) when content truly changed |
| "Thumbnail is not the exact frame I asked for" | exact: false snaps to nearest keyframe (the fast path) | exact: true only where precision matters; never in feeds |
| "First frame is black" | Many videos open on black; position 0 + keyframe snap lands there | position: Duration(seconds: 1) (still keyframe-fast) |
| "HLS (.m3u8) works on one platform, fails on other" | Stream thumbnailing depends on OS media stack support; both outcomes are classified, bounded failures | Treat HLS tiles as best-effort with an error placeholder, or use direct MP4 URLs |
| "Cancelled request still produced a cache file (Android)" | Android cannot abort MediaMetadataRetriever; the engine detaches the caller and banks the finished frame | By design (sunk cost -> future hit). iOS aborts natively |
| "Portrait video renders sideways/wrong box" | Rotation metadata is applied natively; fit box is display-oriented | Expected correct; if actually sideways, that IS a defect - report upstream with the file |
| "StateError: cache sizing can only be configured before the first request" | maxCacheBytes/maxCacheEntries are locked once the engine initializes | Move the single configure() call to main() before any request |
| "evict/clearCache didn't cancel a running extraction" | Eviction only removes stored entries; in-flight work completes | Cancel via the request handle; or ignore - next request re-checks cache |
invalidSource structurally bad input | assetNotFound wrong key or
missing pubspec asset entry | fileNotFound path missing at key time |
network connectivity/HTTP surfaced | unsupportedMedia no decodable
video track / codec | extractionFailed decoder failed otherwise (also the
fallback for unmapped platform errors) | encodingFailed jpeg/png encode
failed | io local write/rename | timeout deadline exceeded (counted
from native dispatch, not enqueue) | cancelled caller cancelled (never
negative-cached).
Causes and per-code fixes: see the reference file.
activeJobs <= maxConcurrentExtractions; if the UI
janks with 0-2 active jobs and high hit ratio, look elsewhere).integrate-feed-thumbnails skill.clearCache() in production paths, disabling the
negative cache to zero for feeds, or raising concurrency past 2-3 without
bench evidence..pub-cache and note the version."Analytics shows spikes of timeout errors from Android users on one CDN
URL that returns 404." -> Known quirk row 2 + negative cache row 1: the
device retriever swallowed the 404, deadline classified timeout, and each
fast-fail replay during the 60s TTL logged another one. Fix analytics
bucketing; nothing is stuck.
Guides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
npx claudepluginhub omar-hanafy/cached_video_thumbnail --plugin cached-video-thumbnail