{"record":{"id":"36e9ca14f48bff30","repo":"DIYgod/RSSHub","slug":"unhandled-thirdparty-on-link-elem-attr-alia","errorCode":null,"errorMessage":"Unhandled thirdparty on ${link}: ${elem.attr('alias')}","messagePattern":"Unhandled thirdparty on (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"error","filePath":"lib/routes/yuque/utils.ts","lineNumber":57,"sourceCode":"            break;\n        case 'mention':\n            html = `<a href='https://www.yuque.com/${value.login}'>${value.name}</a>`;\n            break;\n        case 'table':\n            html = value.html;\n            break;\n        case 'thirdparty':\n        case 'youku':\n            // YES, youku name with bilibli iframe\n            // https://www.yuque.com/api/docs/nn5lyk?book_id=297292&include_contributors=true\n            if (elem.attr('alias') === 'music163') {\n                html = `<iframe frameborder=\"no\" border=\"0\" marginwidth=\"0\" marginheight=\"0\" height=66 src=\"${value.src}\"></iframe>`;\n            } else if (elem.attr('alias') === 'bilibili' || elem.attr('alias') === undefined) {\n                html = `<iframe src=\"${value.src}&high_quality=1&autoplay=0\" width=\"650\" height=\"477\" scrolling=\"no\" border=\"0\" frameborder=\"no\" framespacing=\"0\" allowfullscreen=\"true\"></iframe>`;\n            } else if (value.type === 'codepen') {\n                html = `<iframe height=\"265\" style=\"width: 100%;\" scrolling=\"no\" title=\"codepen\" src=\"${value.url}\" frameborder=\"no\" allowtransparency=\"true\" allowfullscreen=\"true\"></iframe>`;\n            } else {\n                throw new Error(`Unhandled thirdparty on ${link}: ${elem.attr('alias')}`);\n            }\n            break;\n        case 'yuque':\n            if (value.mode === 'card') {\n                html = `<a href='${value.src}'>${value.detail.title}</a>`;\n            } else if (value.mode === 'embed') {\n                html = `<iframe src=\"${value.url}\" width=\"100%\" height=\"518\"></iframe>`;\n            } else {\n                throw new Error(`Unhandled mode on ${link}: ${value.mode}`);\n            }\n            break;\n        case 'video':\n            // fake video src\n            html = `<video src='${value.videoId}'></video>`;\n            break;\n\n        default:\n            throw new Error(`Unhandled card on ${link}: ${name}`);","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/yuque/utils.ts#L39-L75","documentation":"Thrown by the `card2Html` helper in the Yuque route while converting a Yuque `<card name=\"thirdparty\">` (or `youku`) embed into HTML. The code only knows how to render the aliases `music163`, `bilibili`/`undefined`, and the `codepen` type; any other alias or type falls through to this throw. The message echoes the document `link` and the offending `alias` so a maintainer can identify which new third-party platform Yuque started supporting.","triggerScenarios":"A Yuque document embeds a third-party widget whose `alias` attribute is something other than `music163` / `bilibili` / `undefined`, and whose parsed `value.type` is not `codepen` — e.g. a newly added platform like CodeSandbox, Figma, or a video host Yuque integrated after this branch was written.","commonSituations":"Yuque ships a new third-party embed integration; the alias naming for an existing platform changes; the `value.type === 'codepen'` check stops matching because Yuque restructured the card payload. Any single affected document breaks the whole feed because the throw is not caught.","solutions":["Read the `alias` value from the error message and add a new `else if` branch in the `thirdparty`/`youku` case that builds the correct iframe/HTML for that platform (typically using `value.src` or `value.url`).","If the new embed is iframe-based, reuse the existing iframe template and swap in `value.src`/`value.url`.","To stop one unknown embed from breaking the entire document feed, make the `else` branch skip the card (e.g. `html = ''` or leave `elem` in place) instead of throwing, and log the alias for follow-up."],"exampleFix":"// before\n} else {\n    throw new Error(`Unhandled thirdparty on ${link}: ${elem.attr('alias')}`);\n}\n// after (add a branch for the new alias)\n} else if (elem.attr('alias') === 'codesandbox') {\n    html = `<iframe src=\"${value.src}\" style=\"width:100%;height:500px;border:0;border-radius:4px;overflow:hidden;\" allowfullscreen></iframe>`;\n} else {\n    html = '';\n}","handlingStrategy":"try-catch","validationCode":"// Before calling card2Html, snapshot which card names/aliases appear\nconst knownThirdpartyAliases = new Set(['music163', 'bilibili', undefined]);\nconst knownThirdpartyTypes = new Set(['codepen']);\nfunction isHandledThirdparty(elem, value) {\n    return knownThirdpartyAliases.has(elem.attr('alias')) || knownThirdpartyTypes.has(value.type);\n}","typeGuard":"function isHandledThirdparty(elem: cheerio.Cheerio<unknown>, value: { type?: string }): boolean {\n    const alias = elem.attr('alias');\n    return alias === 'music163' || alias === 'bilibili' || alias === undefined || value.type === 'codepen';\n}","tryCatchPattern":"// Wrap per-card conversion so one unhandled embed never kills the doc\ntry {\n    card2Html(elem, link);\n} catch (e) {\n    logger.warn(`Skipping unhandled yuque thirdparty card on ${link}: ${(e as Error).message}`);\n    elem.remove();\n}","preventionTips":["Wrap each card2Html call in try/catch so a single unknown embed degrades to a skipped card instead of failing the whole document.","Maintain a known-types set and log the first occurrence of any new alias/type so you discover schema drift early.","When adding a new third-party embed, add a regression document (a Yuque doc using it) to your test fixtures."],"tags":["yuque","content-parsing","embed","thirdparty","switch-enum"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}