DIYgod/RSSHub · error · Error
failed to parse AJAX response
Error message
failed to parse AJAX response
What it means
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.
Source
Thrown at lib/routes/gc.ca/pm-news.ts:44
],
name: 'News',
maintainers: ['elibroftw'],
handler: async (ctx: Context): Promise<Data> => {
const { language = 'en' } = ctx.req.param();
const ajaxURL = language === 'fr' ? 'https://www.pm.gc.ca/fr/views/ajax' : 'https://www.pm.gc.ca/views/ajax';
const response = 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',
},
});
const replaceItem = response.find((item: any) => item.method === 'replaceWith');
if (!replaceItem) {
throw new Error('failed to parse AJAX response');
}
const $ = load(replaceItem.data);
const items: DataItem[] = $('.news-row')
.toArray()
.map((element) => {
const $element = $(element);
const $titleLink = $element.find('.title a');
const $category = $element.find('.category');
const $date = $element.find('.location-date time');
const title = $titleLink.text().trim();
const link = $titleLink.attr('href')!;
const category = $category.text().trim();
const date = $date.attr('datetime') || '';
if (title && link) {
return {View on GitHub (pinned to bed535e087)
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.
Example fix
// before
const replaceItem = response.find((item: any) => item.method === 'replaceWith');
if (!replaceItem) {
throw new Error('failed to parse AJAX response');
}
// after
const replaceItem = response.find((item: any) => item.method === 'replaceWith');
if (!replaceItem) {
const methods = Array.isArray(response) ? response.map((i) => i?.method).join(', ') : 'non-array response';
throw new Error(`failed to parse AJAX response. Observed methods: ${methods}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
import ofetch from '@/utils/ofetch';
const 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' } });
if (!Array.isArray(probe) || !probe.some((i) => i?.method === 'replaceWith')) {
throw new Error('Drupal AJAX shape changed; inspect raw response');
} Type guard
const hasReplaceWith = (res: unknown): res is Array<{ method: string; data: string }> =>
Array.isArray(res) && res.some((i: any) => i?.method === 'replaceWith' && typeof i.data === 'string'); Try / catch
try {
const replaceItem = response.find((item: any) => item.method === 'replaceWith');
if (!replaceItem) throw new Error('failed to parse AJAX response');
} catch (e) {
// fall back to scraping the news page HTML directly
throw e;
} Prevention
- Inspect the raw AJAX response after any pm.gc.ca redesign. Parameterize view_name/view_display_id so they can be updated without code changes. Maintain an HTML-scrape fallback.
When it happens
Trigger: 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.
Common situations: Drupal major-version upgrades; site redesigns renaming the news view; locale differences (fr vs en endpoint) returning different structures; caching layers stripping the response.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to extract posts from the Next.js RSC payload
- Failed to parse blogList from RSC data
- Cannot find n-token
- this route is empty, please check the original site or <a hr
- Unknown type: ${item.type}
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/f94f0f8da9cee575.
Report an issue: GitHub.