Owner: Engineering Team | Last Updated: 2026-01-30 | Status: Current
This page catalogs WWAI-specific error patterns organized by subsystem. Each entry includes the HTTP status code or error identifier, the exact error message or detail value returned by the API, the root cause, and the recommended solution.
For general debugging strategy, see Debugging Guide. For environment setup issues, see Local Setup.
Authentication flows through NextAuth with JWT tokens issued by the Django backend. The access token has a 1-hour expiry. NextAuth auto-refreshes it 5 minutes before expiry via POST /api/user/refresh/.
Source files: server/auth.ts, NextAuth middleware
| Field | Value |
|---|---|
| Status | 401 |
| Message | Varies by backend response |
| Root Cause | The JWT access token has passed its 1-hour TTL. NextAuth attempts automatic refresh by calling POST /api/user/refresh/ when the token is within 5 minutes of expiry. If the user's tab was inactive or the refresh call itself failed, the token expires fully. |
| Solution | Under normal operation, no action is needed --- NextAuth handles refresh automatically. If users report repeated 401s, check that the backend refresh endpoint is healthy and that NEXTAUTH_SECRET is consistent across deployments. |
| Field | Value |
|---|---|
| Status | N/A (internal NextAuth error) |
| Message | RefreshAccessTokenError |
| Root Cause | The automatic token refresh via POST /api/user/refresh/ failed. This is a special NextAuth error string. The middleware checks token.error !== 'RefreshAccessTokenError' to decide whether the session is valid. The session callback also inspects this value and returns an empty session object when present. Common causes: backend down, refresh token revoked, or refresh token also expired. |
| Solution | The user is automatically redirected to /login to re-authenticate. If this happens frequently, investigate backend uptime and refresh token TTL configuration. Check Django logs for the refresh endpoint response. |
| Field | Value |
|---|---|
| Status | 401 |
| Message | Varies (Invalid token, Token not valid, etc.) |
| Root Cause | The JWT is structurally invalid, was tampered with, or was signed with a different secret. This can occur after a backend secret rotation or if a user manually modified their token. |
| Solution | Clear the user's session (sign out and sign back in). After a backend secret rotation, all existing tokens become invalid --- communicate this to users or trigger a forced re-login. |
| Field | Value |
|---|---|
| Status | 400 |
| Message | OAuth provider returns redirect URI mismatch error |
| Root Cause | The callback URL configured in the OAuth provider console (Google, Facebook) does not match the URL NextAuth is using. |
| Solution | Ensure the provider console has the exact callback URLs registered: |
# Google
http://localhost:3000/api/auth/callback/google (development)
https://yourdomain.com/api/auth/callback/google (production)
# Facebook
http://localhost:3000/api/auth/callback/facebook (development)
https://yourdomain.com/api/auth/callback/facebook (production)
| Field | Value |
|---|---|
| Status | 403 |
| Message | CSRF token validation failed |
| Root Cause | NEXTAUTH_URL does not match the actual domain the browser is accessing, causing cookie domain mismatches. The CSRF token set in the cookie does not match the one submitted with the form. |
| Solution | Verify that NEXTAUTH_URL in .env exactly matches the domain users access (including protocol and port). For local development: NEXTAUTH_URL=http://localhost:3000. For production, ensure it matches the public-facing URL with HTTPS. |
The humanizer is the core feature. Errors originate from both frontend validation and backend API responses. The frontend components HumanizeButton.tsx and HumanizerLayout.tsx handle these error states.
Source files: HumanizeButton.tsx, HumanizerLayout.tsx, app/api/actions.ts
| Field | Value |
|---|---|
| Status | 403 |
| Detail | "Your subscription is paused" |
| Root Cause | The user's subscription has been paused (e.g., payment failure, manual pause). The backend returns this specific detail string. |
| Solution | The frontend displays a pause modal prompting the user to reactivate their subscription. No engineering action needed unless the modal is not appearing. Check the pause modal component renders correctly when this detail is received. |
| Field | Value |
|---|---|
| Status | 403 |
| Detail | "word_limit" or "Your content exceeds the limit of your plan" |
| Root Cause | The submitted text exceeds the maximum words-per-request allowed by the user's subscription plan. Two different detail strings are returned depending on the backend code path, but both indicate the same issue. |
| Solution | The user must reduce the text length or upgrade to a plan with a higher per-request word cap. The frontend should display the plan's word limit so the user knows the threshold. Check plan configuration in the admin panel if the limit seems incorrect. |
| Field | Value |
|---|---|
| Status | 403 |
| Detail | "You have not enough credits" |
| Root Cause | The user's word credit balance is insufficient for the submitted text. Credits are consumed per humanization request based on word count. |
| Solution | The user needs to purchase additional credits or wait for their monthly credit renewal. The frontend should show remaining credits before submission to prevent this error. |
| Field | Value |
|---|---|
| Status | 429 |
| Message | Rate limit exceeded |
| Root Cause | Too many humanization requests in a short period. Backend enforces rate limits per user/IP. |
| Solution | Implement exponential backoff on the frontend. If a legitimate user hits this frequently, review rate limit thresholds in the backend configuration. |
| Field | Value |
|---|---|
| Status | 500 |
| Message | Internal server error |
| Root Cause | The humanization model or backend processing failed. This could be an ML model timeout, out-of-memory error, or unhandled exception. |
| Solution | Check Sentry for the full stack trace. Review backend logs for the specific request. Common causes: model service is down, GPU memory exhausted, or input triggered an edge case in preprocessing. |
The AI detection feature validates text against multiple detection engines. It has its own credit system separate from the humanizer.
| Field | Value |
|---|---|
| Status | N/A (frontend validation) |
| Message | Minimum 50 words required |
| Root Cause | The detector requires at least 50 words to produce a meaningful AI probability score. The frontend validates this before making any API call. |
| Solution | This is expected behavior. The user must provide at least 50 words. No backend call is made. |
| Field | Value |
|---|---|
| Status | 402 |
| Message | Insufficient detector credits |
| Root Cause | The user's detector credit balance is exhausted. Detector credits are tracked separately from humanizer word credits. |
| Solution | The user must purchase additional detector credits. Verify the credit deduction logic is correct --- each detection request should consume one credit regardless of text length. |
| Field | Value |
|---|---|
| Status | Varies (may not be an HTTP error) |
| Message | Individual detector scores may return false or null instead of a numeric score |
| Root Cause | Some external detection services (third-party APIs) may timeout or return non-standard responses. The backend handles this gracefully by substituting false or null for the score. |
| Solution | The frontend should handle false/null scores by showing "N/A" or "Unavailable" for that specific detector rather than treating it as a failure. If a specific detector consistently times out, check its API health or increase the timeout threshold in backend configuration. |
Payment processing occurs in the checkout flow. Errors are extracted from Axios responses and displayed via toast notifications.
Source files: Checkout.tsx
| Field | Value |
|---|---|
| Status | Varies |
| Message | Extracted from e?.response?.data?.error |
| Root Cause | Payment processing failed. Could be: declined card, invalid payment method, payment gateway timeout, or backend validation failure (e.g., invalid coupon code, plan no longer available). |
| Solution | The error message is shown to the user via a toast notification. All checkout errors are logged to Sentry with component and action tags for triage. Check Sentry for the specific error. Common fixes: verify payment gateway API keys, check plan/pricing configuration, review the user's payment method status. |
Common errors encountered during npm run build or npm run dev.
| Field | Value |
|---|---|
| Error | FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory |
| Root Cause | The Next.js build process exceeded the default Node.js memory limit (~1.7 GB). The WWAI codebase is large enough to hit this during production builds. |
| Solution | Set the Node.js memory limit before building: |
NODE_OPTIONS="--max-old-space-size=4096" npm run build
Add this to your CI/CD pipeline and local .env if needed.
| Field | Value |
|---|---|
| Error | Module not found: Can't resolve 'package-name' |
| Root Cause | Dependencies are missing or node_modules is corrupted. Often happens after switching branches with different dependency trees or after a failed install. |
| Solution | Clean install dependencies: |
rm -rf node_modules
npm install
If the error persists, check that the import path is correct and the package is listed in package.json.
| Field | Value |
|---|---|
| Error | Type 'X' is not assignable to type 'Y' (and similar TS errors) |
| Root Cause | TypeScript compilation fails due to type mismatches. Can be caused by updated dependencies with changed type definitions, incorrect prop types, or missing type assertions. |
| Solution | Run the type checker in isolation to see all errors: |
npx tsc --noEmit
Fix errors one at a time. For dependency-related type issues, check if @types/* packages need updating.
| Field | Value |
|---|---|
| Error | Styles missing at runtime; no build error |
| Root Cause | Tailwind's content purge paths in tailwind.config.ts do not include the directories where components are defined, so Tailwind removes the "unused" classes at build time. |
| Solution | Verify the content array in tailwind.config.ts includes all relevant paths: |
content: [
'./app/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./src/**/*.{js,ts,jsx,tsx}',
]
| Field | Value |
|---|---|
| Error | Failed to fetch dynamically imported module / ChunkLoadError |
| Root Cause | A code-split chunk failed to load at runtime. This typically occurs after a deployment when the user's browser has cached old HTML referencing chunk hashes that no longer exist on the server. |
| Solution | The ChunkErrorHandler.tsx component handles this automatically by reloading the page once. If a user reports persistent chunk errors, they should hard-refresh (Ctrl+Shift+R) or clear their browser cache. If the error occurs immediately after a deploy, verify that the CDN/hosting is serving the new assets correctly. |
General patterns that apply across the application.
Source file: app/api/actions.ts
The generic apiCall() function wraps all Axios requests to the backend. It has special handling for 401 errors:
{ error: { status: 401, message: 'Your session expired!' } } instead of throwing. This allows calling code to handle session expiry gracefully (e.g., redirect to login) rather than crashing.This means you should always check for the error property in the response before accessing data:
const result = await apiCall('/some/endpoint')
if (result.error) {
if (result.error.status === 401) {
// Session expired, redirect to login
}
return
}
// Use result.data safely
| Field | Value |
|---|---|
| Status | Browser blocks the request (no HTTP status) |
| Message | Access to XMLHttpRequest at '...' from origin '...' has been blocked by CORS policy |
| Root Cause | The frontend origin is not listed in Django's CORS_ALLOWED_ORIGINS. |
| Solution | Add the frontend URL to CORS_ALLOWED_ORIGINS in the Django backend settings. For local development, ensure http://localhost:3000 is included. |
| Field | Value |
|---|---|
| Status | 404 |
| Message | Not found |
| Root Cause | Django's APPEND_SLASH setting redirects URLs without a trailing slash to the slashed version. If the frontend calls /api/endpoint instead of /api/endpoint/, the redirect may not preserve the request method or body, resulting in a 404. |
| Solution | Ensure all API URLs in the frontend include a trailing slash. |
The codebase uses two standard patterns for extracting error messages from API responses. Use these patterns consistently when adding new error handling.
Used in components that make API calls via Axios directly:
if (axios.isAxiosError(error)) {
const status = error.response?.status
const message =
error.response?.data?.message ||
error.response?.data?.error ||
error.response?.data?.detail ||
t('fallback')
}
The priority order is: message > error > detail > translated fallback string.
Used in components that use the native Fetch API:
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
const errorMessage =
errorData.detail ||
errorData.message ||
errorData.error ||
t('errorOccurred')
}
Note the different priority order for Fetch: detail > message > error > translated fallback. This is because the Django backend primarily uses detail as the error field name (following DRF conventions), and Fetch-based code was written closer to the backend conventions.
Source file: utils/sentryLogger.ts
process.env.NODE_ENV !== 'production'), the logger returns early without sending anything.networkerror / network errorfailed to fetchload failedhydration failed / hydration errorscript error.resizeobserverimport { logError } from '@/utils/sentryLogger'
logError(error, {
tags: {
component: 'HumanizeButton',
action: 'humanize',
},
context: {
userId: user.id,
email: user.email,
},
})
Always include component and action tags so errors can be filtered and triaged effectively in the Sentry dashboard.
If you are investigating an error in production and cannot find it in Sentry, check whether the error message matches one of the ignored patterns listed above. These errors are intentionally suppressed because they are caused by network conditions, browser extensions, or hydration race conditions that are not actionable.
| Status | Context | Detail / Message | Category |
|---|---|---|---|
400 |
Auth | OAuth redirect mismatch | Authentication |
401 |
Any API call | Token expired or invalid | Authentication |
402 |
Detector | Insufficient detector credits | Detector |
403 |
Auth | CSRF token mismatch | Authentication |
403 |
Humanizer | "Your subscription is paused" |
Humanizer |
403 |
Humanizer | "word_limit" |
Humanizer |
403 |
Humanizer | "Your content exceeds the limit of your plan" |
Humanizer |
403 |
Humanizer | "You have not enough credits" |
Humanizer |
404 |
Any API call | Missing trailing slash | Runtime |
429 |
Humanizer | Rate limited | Humanizer |
500 |
Humanizer | Server/model failure | Humanizer |
| N/A | NextAuth | RefreshAccessTokenError |
Authentication |
| N/A | Detector | Score returns false/null |
Detector |
| N/A | Build | Heap out of memory | Build |
| N/A | Runtime | Chunk load failed | Build |
| Environment Issues | Environment-specific errors |
| Deployment Issues | Deployment failures |
| Web App Troubleshooting | Web app specific issues |
| Date | Author | Change |
|---|---|---|
| 2026-01-30 | Engineering | Expanded with WWAI-specific error patterns, root causes, and solutions from codebase analysis |
| 2026-01-30 | Admin | Initial creation |
Next: Environment Issues | Up: General