jackwener/OpenCLI · error · CommandExecutionError
SPA navigation to notifications failed. Twitter may have cha
Error message
SPA navigation to notifications failed. Twitter may have changed its routing.
What it means
CommandExecutionError thrown when SPA navigation to https://x.com/notifications did not land on the /notifications path. The CLI triggers a client-side route change via history.pushState + popstate, waits for capture, then reads window.location.pathname; if it differs from '/notifications' the routing change failed, likely because Twitter changed its SPA routing or an interstitial intercepted navigation.
Source
Thrown at clis/twitter/notifications.js:30
{ name: 'limit', type: 'int', default: 20, help: 'Maximum number of notifications to return (default 20).' },
],
columns: ['id', 'action', 'author', 'text', 'url'],
func: async (page, kwargs) => {
// 1. Navigate to home first (we need a loaded Twitter page for SPA navigation)
await page.goto('https://x.com/home');
await page.wait(3);
// 2. Install interceptor BEFORE SPA navigation
await page.installInterceptor('NotificationsTimeline');
// 3. SPA navigate to notifications via history API
await page.evaluate(`() => {
window.history.pushState({}, '', '/notifications');
window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));
}`);
await page.waitForCapture(5);
// Verify SPA navigation succeeded
const currentUrl = await page.evaluate('() => window.location.pathname');
if (currentUrl !== '/notifications') {
throw new CommandExecutionError('SPA navigation to notifications failed. Twitter may have changed its routing.');
}
// 4. Scroll to trigger pagination
await page.autoScroll({ times: 2, delayMs: 2000 });
// 5. Retrieve data
const requests = await page.getInterceptedRequests();
if (!requests || requests.length === 0)
return [];
let results = [];
const seen = new Set();
for (const req of requests) {
try {
// GraphQL response: { data: { viewer: ... } } (one level of .data)
let instructions = [];
if (req.data?.viewer?.timeline_response?.timeline?.instructions) {
instructions = req.data.viewer.timeline_response.timeline.instructions;
}
else if (req.data?.viewer_v2?.user_results?.result?.notification_timeline?.timeline?.instructions) {
instructions = req.data.viewer_v2.user_results.result.notification_timeline.timeline.instructions;View on GitHub (pinned to 49907e53dc)
Solutions
- Ensure the browser session is authenticated so /notifications does not redirect to /login.
- Log the actual currentUrl to see where navigation landed and adapt the expected path.
- Update the opencli twitter CLI if X renamed its notification route (check for updates/issues).
- As a fallback, navigate directly with page.goto('https://x.com/notifications') and re-run, then report persistent routing failures.
Example fix
// before
if (currentUrl !== '/notifications') {
throw new CommandExecutionError('SPA navigation to notifications failed. Twitter may have changed its routing.');
}
// after
if (currentUrl !== '/notifications') {
await page.goto('https://x.com/notifications');
const fallbackUrl = await page.evaluate('() => window.location.pathname');
if (!fallbackUrl.startsWith('/notifications')) {
throw new CommandExecutionError(`Notifications navigation failed, landed on: ${fallbackUrl}`);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
const path_ = await page.evaluate('() => window.location.pathname');
if (!path_.startsWith('/notifications')) {
throw new Error(`Not on notifications (at ${path_}); check login state and route`);
} Try / catch
try {
const notifs = await twitterNotifications();
} catch (err) {
if (err.message.includes('SPA navigation to notifications failed')) {
console.error('Check that the session is logged in and X routing is unchanged; try direct page.goto fallback');
} else throw err;
} Prevention
- Ensure an authenticated session so /notifications doesn't redirect to /login
- Log the post-navigation pathname to detect X route changes early
- Pin/track CLI updates when X changes its SPA routing
When it happens
Trigger: After dispatching the synthetic popstate, page.evaluate('() => window.location.pathname') returned something other than '/notifications' — e.g. '/home', '/login', or an error route — because X changed route names, the session is unauthenticated, or the SPA redirected.
Common situations: Twitter/X renaming or restructuring notification routes; logged-out sessions being redirected to /login; regional/variant redirects; the SPA boot failing so the router never applies the pushed state.
Related errors
- Unexpected 12306 probe: ${JSON.stringify(probe)}
- Waiting for 12306 tk auth cookie
- x.com
- Nothing changed. Open the tweet in the browser and retry.
- Nothing changed. Open the tweet in the browser and retry.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/bfecf49fdb092bc4.
Report an issue: GitHub.