{"record":{"id":"acda795e58887a58","repo":"maotoumao/MusicFree","slug":"panel-associatelrc-targetexpired","errorCode":null,"errorMessage":"panel.associateLrc.targetExpired","messagePattern":"panel\\.associateLrc\\.targetExpired","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/components/panels/types/associateLrc.tsx","lineNumber":51,"sourceCode":"            height={vmax(30)}\n            renderBody={() => (\n                <>\n                    <PanelHeader\n                        title={t(\"panel.associateLrc.title\")}\n                        onCancel={hidePanel}\n                        onOk={async () => {\n                            const inputValue =\n                                input ?? (await Clipboard.getString());\n                            if (inputValue) {\n                                try {\n                                    const targetMedia = parseMediaUniqueKey(\n                                        inputValue.trim(),\n                                    );\n                                    // 目标也要写进去\n                                    const targetCache =\n                                        mediaCache.getMediaCache(targetMedia);\n                                    if (!targetCache) {\n                                        Toast.warn(\n                                            t(\"panel.associateLrc.targetExpired\"),\n                                        );\n                                        // TODO: ERROR CODE\n                                        throw new Error(\"CLIPBOARD TIMEOUT\");\n                                    }\n\n                                    lyricManager.associateLyric(musicItem, {\n                                        ...targetMedia,\n                                        ...targetCache,\n                                    });\n                                    Toast.success(t(\"panel.associateLrc.toast.success\"));\n                                    hidePanel();\n                                } catch (e: any) {\n                                    if (e.message !== \"CLIPBOARD TIMEOUT\") {\n                                        Toast.warn(t(\"panel.associateLrc.toast.fail\"));\n                                    }\n                                    errorLog(\"关联歌词失败\", e?.message);\n                                }","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/maotoumao/MusicFree/blob/d118b18b3d0c904400f7eea7bf99c0ceec6c1aee/src/components/panels/types/associateLrc.tsx#L33-L69","documentation":"When associating a lyric, the panel parses a unique key from the clipboard/input and looks it up in mediaCache. If no cached media entry exists for that key, the target media has 'expired' from cache, so association is aborted: a warning toast is shown and a sentinel Error('CLIPBOARD TIMEOUT') is thrown to signal the specific failure (so the generic fail toast at line 66 is suppressed).","triggerScenarios":"onOk in AssociateLrc (associateLrc.tsx:39-75) runs parseMediaUniqueKey(inputValue.trim()) and mediaCache.getMediaCache(targetMedia) returns undefined — i.e. the clipboard contains a media unique key whose cached entry was evicted or never existed (cache cleared, app restarted, or clipboard text is not a valid cached key).","commonSituations":"User copies a media key, clears app data/cache or restarts the app, then tries to associate lyrics; pasting arbitrary text that parses as a key but has no cache entry; mediaCache eviction after long sessions.","solutions":["Re-open the source media (play/search it) so mediaCache re-caches the entry, then retry the association.","Paste the full media info instead of relying on a stale unique key from the clipboard.","Type the key into the input field rather than falling back to Clipboard.getString() to avoid stale clipboard content.","Developer: extend mediaCache TTL or persist cache entries across restarts."],"exampleFix":"// before\nconst targetCache = mediaCache.getMediaCache(targetMedia);\nif (!targetCache) {\n    Toast.warn(t(\"panel.associateLrc.targetExpired\"));\n    throw new Error(\"CLIPBOARD TIMEOUT\");\n}\n// after\nconst targetCache = mediaCache.getMediaCache(targetMedia);\nif (!targetCache) {\n    const refreshed = await mediaCache.addMusicToCache(targetMedia); // rehydrate\n    if (!refreshed) {\n        Toast.warn(t(\"panel.associateLrc.targetExpired\"));\n        throw new Error(\"CLIPBOARD TIMEOUT\");\n    }\n}","handlingStrategy":"validation","validationCode":"const targetMedia = parseMediaUniqueKey(inputValue.trim());\nconst targetCache = targetMedia ? mediaCache.getMediaCache(targetMedia) : undefined;\nif (!targetCache) {\n    // re-open/re-play the target media first, or abort before associating\n    return;\n}","typeGuard":"function hasCachedMedia(key: unknown): key is IMedia.MediaUniqueKey {\n    return typeof key === 'string' && mediaCache.getMediaCache(parseMediaUniqueKey(key)) != null;\n}","tryCatchPattern":"try {\n    // association logic\n} catch (e: any) {\n    if (e?.message === \"CLIPBOARD TIMEOUT\") {\n        // cache-miss path already toasted; do not double-toast\n    } else {\n        Toast.warn(t(\"panel.associateLrc.toast.fail\"));\n    }\n}","preventionTips":["Re-open the target media before associating so its cache entry exists.","Avoid depending on stale clipboard content from previous app sessions.","Clear the clipboard fallback when the input field has content.","Extend mediaCache retention if associations happen long after playback."],"tags":["clipboard","cache-miss","lyrics","toast"],"backgroundTag":"cache-entry-expired","analyzedSha":"d118b18b3d0c904400f7eea7bf99c0ceec6c1aee","analyzedAt":"2026-08-30T11:49:12.932Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}