Skip to content

Fix AJAX endpoints returning HTML instead of JSON on transient errors#498

Open
scott2b wants to merge 1 commit into
masterfrom
fix-ajax-html-errors
Open

Fix AJAX endpoints returning HTML instead of JSON on transient errors#498
scott2b wants to merge 1 commit into
masterfrom
fix-ajax-html-errors

Conversation

@scott2b
Copy link
Copy Markdown
Member

@scott2b scott2b commented May 11, 2026

Summary

  • Users intermittently get Unexpected token '<', "<!DOCTYPE "... errors when saving StoryMaps. The error is transient — retrying works. Root cause: transient DB failures or session expiry cause the server to return HTML (Flask default 500 pages or login redirect) instead of JSON, which jQuery can't parse.
  • Server-side: Auth decorators now catch DB exceptions and return JSON 500 for AJAX requests (instead of unhandled HTML 500). Expired sessions return JSON 401 (instead of 302 redirect to HTML login page). Added Flask @app.errorhandler for 401/403/500 as a safety net. Eliminated redundant get_session_user() call in @require_user_id. Added missing @require_user to storymap_export.
  • Client-side: AJAX error handler now detects parseerror (HTML response), HTTP 401/502/503/504, and timeouts — shows actionable messages like "Your session has expired. Reload this page to sign back in." instead of raw JSON parse errors.
  • Deploy: Added gunicorn --timeout 90 (was default 30s, shorter than S3's 60s read timeout) and nginx proxy_read_timeout 95s to prevent worker kills during slow S3 operations.

Test plan

  • All 9 unit tests pass (6 new tests for AJAX error handling, 3 existing)
  • Deploy to staging and verify:
    • Clear session cookie → click save → modal shows "session expired" message (not raw parse error)
    • Normal save workflow unchanged
    • Kill a gunicorn worker mid-request → user sees "temporary server error" message
  • Verify nginx/gunicorn restart picks up new timeout values

🤖 Generated with Claude Code

Users intermittently see "Unexpected token '<', <!DOCTYPE..." when saving
because transient DB failures or session issues cause the server to return
HTML (Flask default error pages or login redirects) instead of JSON.

Server-side: add AJAX detection, return JSON errors from auth decorators
and Flask error handlers, eliminate redundant DB call in require_user_id.
Client-side: detect HTML/parseerror responses and show actionable messages.
Deploy: align gunicorn timeout (90s) with S3 timeout (60s) and nginx (95s).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@scott2b scott2b self-assigned this May 11, 2026
@scott2b scott2b added the bug label May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant