{"record":{"id":"b28e461092594e60","repo":"DIYgod/RSSHub","slug":"unhandled-mark-type-mark-type","errorCode":null,"errorMessage":"Unhandled mark type: ${mark.type}","messagePattern":"Unhandled mark type: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"error","filePath":"lib/routes/netflix/newsroom.ts","lineNumber":142,"sourceCode":"            if (!node.marks || node.marks.length === 0) {\n                return text;\n            }\n            for (const mark of node.marks) {\n                switch (mark.type) {\n                    case 'bold':\n                        text = `<strong>${text}</strong>`;\n                        break;\n                    case 'italic':\n                        text = `<em>${text}</em>`;\n                        break;\n                    case 'underline':\n                        text = `<u>${text}</u>`;\n                        break;\n                    case 'strikethrough':\n                        text = `<s>${text}</s>`;\n                        break;\n                    default:\n                        throw new Error(`Unhandled mark type: ${mark.type}`);\n                }\n            }\n            return text;\n        }\n        case 'hyperlink': {\n            const href = node.data?.uri || '#';\n            const innerHTML = node.content?.map((c) => render(c)).join('') || '';\n            return `<a href=\"${href}\" target=\"_blank\" rel=\"noopener noreferrer\">${innerHTML}</a>`;\n        }\n\n        case 'embedded-asset-block': {\n            const file = Object.values<any>(node.data?.file)[0];\n            if (!file || !file.url) {\n                return '';\n            }\n\n            const url = file.url.startsWith('//') ? 'https:' + file.url : file.url;\n            const contentType = file.contentType || '';","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/netflix/newsroom.ts#L124-L160","documentation":"Thrown (bare Error) inside the recursive render() function of the Netflix newsroom route when a rich-text 'text' node has a mark whose type is not one of the four handled: bold, italic, underline, strikethrough. The Netflix/Contentful-style document model added (or returned) an unrecognised inline mark (e.g. 'code', 'superscript', 'subscript', 'comment'), and the default branch of the switch throws instead of rendering or skipping it.","triggerScenarios":"While rendering an article's rich-text body, a text node carries node.marks = [{type: 'code'}] (or any non-handled type). The switch falls through to default and throws, failing the whole feed even though only one article uses the new mark. Most likely after Netflix/CMS adds a new formatting option.","commonSituations":"Netflix's CMS introduces a new text decoration; an article copy-pasted from another tool carries an unusual mark; a Contentful schema upgrade adds marks the renderer doesn't know.","solutions":["Patch the switch to handle (or ignore) the new mark type — add cases for the missing types, and make the default return the text unmodified instead of throwing, so future marks don't break the feed.","Inspect the failing article's JSON (fetch the upstream payload) to see exactly which mark.type triggered it.","If you only need a quick unblock, default to returning `text` (no formatting) for unknown marks."],"exampleFix":"// before\ndefault:\n    throw new Error(`Unhandled mark type: ${mark.type}`);\n\n// after — render known extra marks, gracefully ignore unknown ones\ncase 'code':\n    text = `<code>${text}</code>`;\n    break;\ncase 'superscript':\n    text = `<sup>${text}</sup>`;\n    break;\ncase 'subscript':\n    text = `<sub>${text}</sub>`;\n    break;\ndefault:\n    // unknown mark: keep the text, do not fail the whole feed\n    break;","handlingStrategy":"validation","validationCode":"// Defensive: never throw on an unknown mark; render known ones, ignore the rest.\nfor (const mark of node.marks ?? []) {\n    switch (mark.type) {\n        case 'bold': text = `<strong>${text}</strong>`; break;\n        case 'italic': text = `<em>${text}</em>`; break;\n        case 'underline': text = `<u>${text}</u>`; break;\n        case 'strikethrough': text = `<s>${text}</s>`; break;\n        case 'code': text = `<code>${text}</code>`; break;\n        default: break; // ignore unknown marks\n    }\n}","typeGuard":"const KNOWN_MARKS = new Set(['bold', 'italic', 'underline', 'strikethrough', 'code']);\nfunction isKnownMark(mark: any): boolean {\n    return !!mark && typeof mark.type === 'string' && KNOWN_MARKS.has(mark.type);\n}","tryCatchPattern":null,"preventionTips":["Never throw from a recursive rich-text renderer's default branch — degrade gracefully instead.","Pin the renderer to a known CMS schema version and log unknown node/mark types for awareness without failing.","Add tests with sample documents containing exotic marks to prevent regressions."],"tags":["rich-text","schema-change","defensive-default","netflix","contentful"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}