{"record":{"id":"5921cdcb9c919d9e","repo":"DIYgod/RSSHub","slug":"unhandle-asset-type-a-asset-type","errorCode":null,"errorMessage":"Unhandle asset type: ${a.asset_type}","messagePattern":"Unhandle asset type: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":503,"severity":"error","filePath":"lib/routes/artstation/user.ts","lineNumber":106,"sourceCode":"    const items = await Promise.all(\n        list.map((item) =>\n            cache.tryGet(item.link, async () => {\n                if (item.assetsCount > 1 || !item.icons.image) {\n                    const { data } = await got(`https://www.artstation.com/projects/${item.hashId}.json`, {\n                        headers: {\n                            ...headers,\n                            cookie: `PRIVATE-CSRF-TOKEN=${csrfToken}`,\n                        },\n                    });\n\n                    item.description = renderDescription({\n                        description: data.description,\n                        assets: data.assets,\n                    });\n\n                    for (const a of data.assets) {\n                        if (a.asset_type !== 'video' && a.asset_type !== 'image' && a.asset_type !== 'video_clip' && a.asset_type !== 'cover') {\n                            throw new Error(`Unhandle asset type: ${a.asset_type}`); // model3d, marmoset, pano\n                        }\n                    }\n                }\n\n                return item;\n            })\n        )\n    );\n\n    return {\n        title: `${userData.full_name} - ArtStation`,\n        description: userData.headline,\n        link: userData.permalink,\n        logo: userData.large_avatar_url,\n        icon: userData.large_avatar_url,\n        image: userData.default_cover_url,\n        item: items,\n    };","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/artstation/user.ts#L88-L124","documentation":"Thrown by the ArtStation user route while rendering each project's assets. After fetching a project's JSON, the handler iterates data.assets and only knows how to render asset_type values of 'video', 'image', 'video_clip', and 'cover'. Any other asset_type (the comment lists model3d, marmoset, pano) is treated as unrenderable and aborts the whole feed rather than silently dropping content.","triggerScenarios":"A user publishes an ArtStation project containing a 3D model (model3d), a Marmoset Viewer scene (marmoset), or a 360 panorama (pano), and that project is fetched in the detail loop (assetsCount > 1 or no image icon). The loop hits the unhandled asset_type branch and throws.","commonSituations":"Artists uploading 3D/pano content; ArtStation adding a new asset_type to their API that the route was never updated to handle; rendering the feed of a user whose portfolio mixes 2D and 3D work.","solutions":["If you control the route, extend the allow-list to include the new asset_type (e.g. add 'model3d', 'marmoset', 'pano') and provide rendering for it, or downgrade the throw to a continue/skip so one unhandled asset does not kill the feed.","If you are only consuming the feed, filter the offending user or wait for a route update; file an issue quoting the asset_type value from the error message."],"exampleFix":"// before\nfor (const a of data.assets) {\n    if (a.asset_type !== 'video' && a.asset_type !== 'image' && a.asset_type !== 'video_clip' && a.asset_type !== 'cover') {\n        throw new Error(`Unhandle asset type: ${a.asset_type}`);\n    }\n}\n\n// after: skip unknown asset types instead of aborting the feed\nconst KNOWN = new Set(['video', 'image', 'video_clip', 'cover', 'model3d', 'marmoset', 'pano']);\nfor (const a of data.assets) {\n    if (!KNOWN.has(a.asset_type)) {\n        continue;\n    }\n}","handlingStrategy":"validation","validationCode":"const KNOWN_ASSET_TYPES = new Set(['video', 'image', 'video_clip', 'cover']);\nconst unknown = (data.assets ?? []).filter((a) => !KNOWN_ASSET_TYPES.has(a.asset_type));\nif (unknown.length) {\n    // log/branch before the throw site\n    logger.warn(`Skipping ${unknown.length} unhandled asset(s): ${unknown.map((a) => a.asset_type).join(', ')}`);\n}","typeGuard":"const KNOWN_ASSET_TYPES = new Set(['video', 'image', 'video_clip', 'cover']);\nfunction isKnownAsset(a: { asset_type: string }): boolean {\n    return KNOWN_ASSET_TYPES.has(a.asset_type);\n}","tryCatchPattern":null,"preventionTips":["Whitelist asset_type with a Set lookup rather than a long && chain so adding a type is a one-line change.","Skip unknown assets (continue) instead of throwing so a single 3D project does not kill the whole feed.","Log the unknown asset_type so maintainers learn about new ArtStation types."],"tags":["artstation","asset-rendering","validation","defensive-check"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}