{"record":{"id":"27820aae2faa69e0","repo":"DIYgod/RSSHub","slug":"unhandled-attachment-type-attachment-contenttyp","errorCode":null,"errorMessage":"Unhandled attachment type: ${attachment.contentType} for post ${attachment.postId}","messagePattern":"Unhandled attachment type: (.+?) for post (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/fansly/utils.tsx","lineNumber":116,"sourceCode":"                case 8: {\n                    // aggregated post (repost)\n                    let attachments = '<br><br>';\n                    const repost = aggregationData.aggregatedPosts.find((post) => post.id === attachment.contentId) || aggregationData.posts.find((post) => post.id === attachment.contentId);\n                    attachments += parseDescription(repost, aggregationData);\n\n                    return attachments;\n                }\n\n                case 7100:\n                    return renderTipGoal(attachment.contentId, aggregationData.tipGoals);\n                case 32001:\n                    // unknown\n                    return '';\n                case 42001:\n                    return renderPoll(attachment.contentId, aggregationData.polls);\n\n                default:\n                    throw new Error(`Unhandled attachment type: ${attachment.contentType} for post ${attachment.postId}`);\n            }\n        })\n        .join('');\n\nconst parseMedia = (contentId, accountMedia) => {\n    const media = accountMedia.find((media) => media.id === contentId);\n    if (!media) {\n        return '';\n    }\n    return renderMedia(media.preview ?? media.media);\n};\n\nconst renderMedia = (media) => {\n    switch (media.mimetype) {\n        case 'image/gif':\n        case 'image/jpeg':\n        case 'image/png':\n        case 'video/mp4':","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/fansly/utils.tsx#L98-L134","documentation":"The Fansly attachment renderer switches on a numeric `contentType` (e.g. 7100 tip goal, 42001 poll, 32001 unknown -> empty string, plus media cases handled above). Any unrecognised contentType hits `default` and throws a generic Error that includes both the contentType and the postId. This is a parser-completeness gap that fails the whole post.","triggerScenarios":"Fansly introduces a new attachment contentType code not listed in the switch.","commonSituations":"An upstream API addition ships a new attachment kind; posts containing it fail to render.","solutions":["Read the contentType from the error message and add a case (or map it to an existing renderer).","As a stopgap, return an empty string in the `default` arm so the post renders without the unknown attachment.","Log the contentType/postId so newly added codes surface quickly."],"exampleFix":"// before\n//   default:\n//       throw new Error(`Unhandled attachment type: ${attachment.contentType} for post ${attachment.postId}`);\n// after\n//   default:\n//       return '';","handlingStrategy":"fallback","validationCode":"const KNOWN_ATTACHMENT_TYPES = new Set([/* media codes */, 7100, 32001, 42001]);\nfunction isKnownAttachment(a: { contentType: number }): boolean {\n  return KNOWN_ATTACHMENT_TYPES.has(a.contentType);\n}","typeGuard":"function isKnownAttachment(a: { contentType: number }): boolean {\n  return KNOWN_ATTACHMENT_TYPES.has(a.contentType);\n}","tryCatchPattern":"try {\n  return renderAttachment(attachment);\n} catch {\n  return '';\n}","preventionTips":["Return '' in the default arm so one unknown attachment cannot fail the whole post.","Log contentType + postId on unknown codes.","Keep a registry of known contentType codes with comments."],"tags":["api","parsing","switch-default","schema-drift"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}