# Error Documentation Pattern Document store errors consistently across Video.js packages. --- ## Error Code Reference Table Always document errors in this format: | Code | Meaning | Recovery | | ------------ | ------------------------------------------- | ---------------------------------------------- | | `ABORTED` | Request aborted via signal | Expected during cleanup — no action needed | | `CANCELLED` | Cancelled by another request's `cancel: []` | Check request coordination | | `SUPERSEDED` | Same-key request replaced this one | Expected during rapid input — no action needed | | `REJECTED` | Guard returned falsy | Check preconditions, show user feedback | | `TIMEOUT` | Guard timed out | Increase timeout or check target readiness | | `NO_TARGET` | No target attached | Call `attach()` before making requests | | `DETACHED` | Target was detached | Re-attach or abort operation | | `DESTROYED` | Store was destroyed | Create new store instance | --- ## Expected vs Unexpected Errors Document which errors are "normal" vs programming errors: | Code | Expected? | Notes | | ------------ | --------- | --------------------------------------------- | | `SUPERSEDED` | Often | Rapid user input (scrubbing, repeated clicks) | | `ABORTED` | Often | Component unmount, navigation | | `CANCELLED` | Sometimes | Intentional coordination between requests | | `REJECTED` | Sometimes | Guard logic blocking execution | | `TIMEOUT` | Rarely | Slow media load, network issues | | `NO_TARGET` | Never | Programming error — attach before use | | `DETACHED` | Rarely | Lifecycle timing issue | | `DESTROYED` | Never | Programming error — don't use after destroy | --- ## Error Handling Patterns ### Global Handler (store config) ```ts const store = createStore({ features: [playbackFeature, volumeFeature], onError: ({ error, request }) => { if (request) { console.error(`${request.name} failed:`, error.code); } // Report to analytics, show toast, etc. }, }); ``` ### Local Handler (try/catch) ```ts import { isStoreError } from '@videojs/store'; try { await store.request.play(); } catch (error) { if (isStoreError(error)) { switch (error.code) { case 'SUPERSEDED': // Another request took over — expected, ignore break; case 'REJECTED': // Guard blocked execution — show feedback showMessage('Cannot play right now'); break; case 'TIMEOUT': // Took too long — retry or show error showMessage('Media not ready'); break; default: console.error(`[${error.code}]`, error.message); } } else { throw error; // Re-throw unknown errors } } ``` ### Type Guard Pattern Always show the type guard: ```ts import { isStoreError } from '@videojs/store'; function handleError(error: unknown) { if (isStoreError(error)) { // error is StoreError — has .code, .message return { code: error.code, message: error.message }; } throw error; } ``` --- ## Troubleshooting Section Format ### Structure 1. Error code/message as heading 2. **Cause:** One sentence 3. **Solution:** Code example ### Examples #### NO_TARGET **Cause:** Request made before `attach()` was called. **Solution:** ```ts // ❌ Wrong const store = createStore({ features: [playbackFeature] }); await store.request.play(); // Error: NO_TARGET // ✅ Correct const store = createStore({ features: [playbackFeature] }); store.attach(videoElement); await store.request.play(); ``` #### SUPERSEDED **Cause:** Another request with the same key started before this one finished. **Solution:** This is usually expected behavior. If you need the result, check before making a new request: ```ts // If you need to know the final state const result = await store.request.play(); // Result may be from a later request if superseded // If you want to prevent supersession, use unique keys request: { trackEvent: { key: () => Symbol(), // Each call gets unique key handler: (data) => analytics.log(data), }, } ``` #### REJECTED **Cause:** A guard returned a falsy value. **Solution:** Check what condition the guard expects: ```ts // Guard that checks readyState const canPlay: Guard = ({ target }) => { return target.readyState >= HTMLMediaElement.HAVE_ENOUGH_DATA; }; // If rejected, media isn't ready — wait for canplay event player.on('canplay', () => { // Now safe to request play store.request.play(); }); ``` #### TIMEOUT **Cause:** A guard didn't resolve within the timeout period. **Solution:** Increase the timeout or ensure the target is ready: ```ts import { timeout } from '@videojs/store'; request: { play: { // Increase timeout for slow connections guard: timeout(canMediaPlay, 10000), // 10 seconds handler: async (_, { target }) => { await target.play(); }, }, } ``` #### ABORTED **Cause:** The abort signal was triggered (usually from component unmount). **Solution:** This is expected behavior. Ensure cleanup runs: ```ts // React useEffect(() => { const controller = new AbortController(); store.request.play(null, { signal: controller.signal }); return () => controller.abort(); // Cleans up on unmount }, []); ``` --- ## API Reference Format When documenting error-related APIs: ### isStoreError Type guard for store errors. ```ts import { isStoreError } from '@videojs/store'; if (isStoreError(error)) { console.log(error.code); // 'ABORTED' | 'CANCELLED' | ... } ``` #### Parameters | Parameter | Type | Description | | --------- | --------- | ---------------- | | `error` | `unknown` | Any caught error | #### Returns `error is StoreError` — Type predicate ### StoreError Error thrown by store operations. #### Properties | Property | Type | Description | | --------- | ---------------- | -------------------------- | | `code` | `StoreErrorCode` | Error classification | | `message` | `string` | Human-readable description | #### StoreErrorCode ```ts type StoreErrorCode = | 'ABORTED' | 'CANCELLED' | 'DESTROYED' | 'DETACHED' | 'NO_TARGET' | 'REJECTED' | 'SUPERSEDED' | 'TIMEOUT'; ``` --- ## Checklist When documenting errors: - [ ] Error code table with all codes - [ ] Expected vs unexpected classification - [ ] Global handler example (onError) - [ ] Local handler example (try/catch) - [ ] Type guard usage shown - [ ] Troubleshooting section for common errors - [ ] Each troubleshooting entry has: Cause + Solution - [ ] Code examples are self-contained