{"record":{"id":"f94f0f8da9cee575","repo":"DIYgod/RSSHub","slug":"failed-to-parse-ajax-response","errorCode":null,"errorMessage":"failed to parse AJAX response","messagePattern":"failed to parse AJAX response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/gc.ca/pm-news.ts","lineNumber":44,"sourceCode":"    ],\n    name: 'News',\n    maintainers: ['elibroftw'],\n    handler: async (ctx: Context): Promise<Data> => {\n        const { language = 'en' } = ctx.req.param();\n\n        const ajaxURL = language === 'fr' ? 'https://www.pm.gc.ca/fr/views/ajax' : 'https://www.pm.gc.ca/views/ajax';\n\n        const response = await ofetch(ajaxURL, {\n            method: 'post',\n            body: new URLSearchParams({ view_name: 'news', view_display_id: 'page_1', view_args: '', page: '0' }).toString(),\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded',\n            },\n        });\n\n        const replaceItem = response.find((item: any) => item.method === 'replaceWith');\n        if (!replaceItem) {\n            throw new Error('failed to parse AJAX response');\n        }\n\n        const $ = load(replaceItem.data);\n        const items: DataItem[] = $('.news-row')\n            .toArray()\n            .map((element) => {\n                const $element = $(element);\n                const $titleLink = $element.find('.title a');\n                const $category = $element.find('.category');\n                const $date = $element.find('.location-date time');\n\n                const title = $titleLink.text().trim();\n                const link = $titleLink.attr('href')!;\n                const category = $category.text().trim();\n                const date = $date.attr('datetime') || '';\n\n                if (title && link) {\n                    return {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/gc.ca/pm-news.ts#L26-L62","documentation":"The Prime Minister of Canada site uses Drupal's Views AJAX module, which returns an array of command objects (each with a method like replaceWith, insert, etc.). The route finds the entry whose method is 'replaceWith' to obtain the HTML fragment. If none exists, the AJAX response shape changed (Drupal core update, view renamed, or an error body was returned), and the route cannot proceed.","triggerScenarios":"pm.gc.ca upgrades Drupal and changes the AJAX command structure; the 'news' view or 'page_1' display is renamed/removed; the endpoint returns an error JSON without command objects; the view_args or view_display_id no longer match the deployed view.","commonSituations":"Drupal major-version upgrades; site redesigns renaming the news view; locale differences (fr vs en endpoint) returning different structures; caching layers stripping the response.","solutions":["POST to the ajaxURL manually (with the same form body) and inspect the raw JSON to see what methods/commands are present.","If the view was renamed, update view_name/view_display_id to match the current site.","If the replaceWith command moved, adjust the find() predicate or fall back to another command carrying the HTML.","Switch to scraping the news page HTML directly if the AJAX layer is unstable."],"exampleFix":"// before\nconst replaceItem = response.find((item: any) => item.method === 'replaceWith');\nif (!replaceItem) {\n    throw new Error('failed to parse AJAX response');\n}\n\n// after\nconst replaceItem = response.find((item: any) => item.method === 'replaceWith');\nif (!replaceItem) {\n    const methods = Array.isArray(response) ? response.map((i) => i?.method).join(', ') : 'non-array response';\n    throw new Error(`failed to parse AJAX response. Observed methods: ${methods}`);\n}","handlingStrategy":"try-catch","validationCode":"import ofetch from '@/utils/ofetch';\nconst probe = await ofetch(ajaxURL, { method: 'post', body: new URLSearchParams({ view_name: 'news', view_display_id: 'page_1', view_args: '', page: '0' }).toString(), headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });\nif (!Array.isArray(probe) || !probe.some((i) => i?.method === 'replaceWith')) {\n  throw new Error('Drupal AJAX shape changed; inspect raw response');\n}","typeGuard":"const hasReplaceWith = (res: unknown): res is Array<{ method: string; data: string }> =>\n  Array.isArray(res) && res.some((i: any) => i?.method === 'replaceWith' && typeof i.data === 'string');","tryCatchPattern":"try {\n  const replaceItem = response.find((item: any) => item.method === 'replaceWith');\n  if (!replaceItem) throw new Error('failed to parse AJAX response');\n} catch (e) {\n  // fall back to scraping the news page HTML directly\n  throw e;\n}","preventionTips":["Inspect the raw AJAX response after any pm.gc.ca redesign.\nParameterize view_name/view_display_id so they can be updated without code changes.\nMaintain an HTML-scrape fallback."],"tags":["drupal","ajax","scraping","fragile"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}