| name | error-fixer |
| description | Fix JavaScript and HTTP errors flagged for AI in the admin dashboard. Queries the database for
errors with ai_status='flagged_for_ai', analyzes them, and attempts to fix them.
Use for: "fix errors"/"fix flagged errors", "check error queue", AI
error-queue / AI-flagged errors, or after flagging errors in the admin
dashboard.
Triggers: "fix errors", "error queue", "ai errors", "flagged errors", "fix ai errors", "/fix-errors"
|
Error Fixer
Fetch and fix errors flagged for AI correction in the admin dashboard. Supports both JS errors (error_reports) and HTTP errors (http_error_logs).
Skill-specific learnings: See references/learnings.md for PostgreSQL error codes, RPC debugging patterns, and gotchas.
Cross-cutting learnings: See .claude/LEARNINGS.md for:
- "Supabase/Database" → RLS+GRANT 403 errors, RPC 404 patterns, CHECK constraint values
- "React/TypeScript" → useEffect infinite loops, null checks
Relation to the AI Index
This skill replaces the AI Index bootstrap for error-fix sessions — do not
re-read Docs/ai-index/manifest.yml#bootstrap.on_error_fix_task when invoked.
The patterns that list points at (silent_error_swallow_pattern,
edge_fn_postgrest_error_masking, and the canonical fixes for both) already
live in references/learnings.md here, where this skill loads them automatically.
The manifest is for sessions that start from a feature/domain question and have
no skill match — it is the fallback orientation layer, not a parallel one.
Workflow
1. FETCH → Get AI-flagged errors from both tables
2. TRIAGE → Identify stale-cache / superseded reports BEFORE deep analysis
3. ANALYZE → Understand each remaining error (stack trace, context, URL, status)
4. FIX → Apply fixes using systematic debugging
5. MARK → Mark errors as ai_fixed with notes
6. REPORT → Summarize what was fixed
7. LEARN → If non-obvious pattern discovered, invoke /opi to capture it
Step 2: Triage stale reports first (cheap dismissal)
A large share of JS reports come from a browser tab still running a pre-fix Vite bundle. Do this before opening any file:
- Decode the bundle timestamp. Every Vite-served module URL in the stack trace has
?t=<ms> (e.g. VerseSetReader.tsx?t=1778490515075). That's Date.now() at the moment Vite last hot-reloaded the file.
- Compare with last commit touching the file:
git log -1 --format="%ci %h %s" -- <file/path/from/stack>
- If the file was committed AFTER the bundle timestamp, the report is stale — mark
ai_fixed with:
Already fixed in commit <sha>. Report came from a browser tab still running
pre-fix Vite bundle (t=<ms>). Hard reload clears it.
- Also dismiss as stale:
CHUNK_LOAD_ERROR "Importing a module script failed" on a route that recently changed (deploy-vs-cache race).
This collapses 3 identical reports of the same hooks/syntax error into a single 30-second triage instead of a full investigation.
Step 1: Fetch Flagged Errors
Query both error tables using Supabase MCP:
JS Errors:
SELECT id, error_type, error_message, stack_trace, context, user_action, ai_prompt, created_at
FROM error_reports WHERE ai_status = 'flagged_for_ai'
HTTP Errors:
SELECT id, method, url, status_code, response_body, request_context, navigation_path, ai_prompt, created_at
FROM http_error_logs WHERE ai_status = 'flagged_for_ai'
Or use RPC functions: get_errors_for_ai() and get_http_errors_for_ai().
Important: The ai_prompt field contains specific instructions from the admin.
Step 2: Analyze Each Error
JS Errors
- Read the ai_prompt - Admin's instruction
- Parse stack trace - File path, line number, call chain
- Check context -
route, action, other data
- Read affected file(s)
HTTP Errors
- Read the ai_prompt - Admin's instruction
- Check URL and status code - What endpoint failed and why
- Review response_body - Error message from server
- Check navigation_path - User's journey to this error
- Find the code - Locate fetch/API call that made this request
Step 4: Fix
Direct fix is the default. Only delegate to a sub-skill when the change is genuinely outside the scope of "edit code + apply migration + redeploy edge function" — e.g., complete RLS redesign, multi-week feature work. For the common case, just apply the fix here.
Common patterns for quick reference
| Error Type | Common Fix |
|---|
TypeError: Cannot read property 'x' of undefined | Add null checks, optional chaining |
TypeError: x is not a function | Check imports; if auth.getClaims is not a function → pinned @supabase/supabase-js@2.49.1 doesn't expose it. Use auth.getUser(token). |
Rendered more hooks than during the previous render | Hooks called after early return — move all hooks above early returns |
ChunkLoadError / Failed to fetch dynamically imported module | Usually stale browser bundle after deploy — see Step 2 triage. If recurrent, check React.lazy imports. |
Identifier 'X' has already been declared | Duplicate declaration — usually a hook destructuring shadowed by a later const X = … |
400 + 23514 check_violation | Read RPC source via SELECT prosrc FROM pg_proc WHERE proname='X', find the failing INSERT/UPDATE. Also check sibling table constraints (e.g. practice.practices mirroring practice.practice_templates). |
400 + 42703 undefined_column | Read actual table schema first. The column may be aggregated on-the-fly by an RPC (completed_days[] from user_reading_progress) rather than stored on the table. |
400 + 42883 undefined_function | RPC calls non-existent function — check function name/args |
400 + 42P13 "Final statement returns too many columns" | A public wrapper does SELECT * FROM bible_schema.X(...) but the inner RPC added columns. DROP+CREATE the wrapper with the new return shape. |
| 400 Bad Request (other) | Validate request params; check payload format |
403 + 42501 insufficient_privilege | Need all three: schema in PostgREST db-schemas, GRANT for authenticated, RLS policy for the operation |
PGRST202 Could not find the function … in the schema cache | Migration drift. Function exists in supabase/migrations/ but not remote. Apply the migration (or add missing param as DEFAULT passthrough if the call shape changed). |
PGRST203 Could not choose the best candidate function | Ambiguous overload. Drop the dead overload; don't try to rename. |
| 401/403 Unauthorized | Check auth token; verify RLS policies |
| 404 Not Found | Verify endpoint exists; check URL construction |
| 500 Server Error (edge function) | mcp__plugin_supabase_supabase__get_logs(service="edge-function") for actual cause. If the response is generic "X failed", fix the catch block to surface detail while you fix the root cause. |
Migration drift check (PGRST202)
When a PGRST202 fires on <rpc_name>:
grep -rl "FUNCTION.*<rpc_name>" supabase/migrations/
If a migration file exists, the diagnosis is "local-only migration was never pushed to remote". Apply it via mcp__plugin_supabase_supabase__apply_migration and verify with pg_proc. No code change needed.
Public-wrapper review (after any bible_schema.X RPC change)
Many frontend calls go through public.X wrappers that do SELECT * FROM bible_schema.X(...). When the inner RPC's return shape changes, the wrapper's RETURNS TABLE(...) desyncs and emits 42P13. After modifying any bible_schema RPC:
SELECT n.nspname, p.proname
FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE p.proname = '<changed_rpc_name>'
If a public row appears, inspect its body — if it's a SELECT * wrapper, drop and recreate it with the new column list.
Step 4: Mark as Fixed
Use direct UPDATE for both tables (the mark_error_ai_fixed RPC requires admin role which MCP SQL lacks):
UPDATE error_reports SET ai_status = 'ai_fixed', ai_fixed_at = NOW(),
ai_fix_notes = 'description' WHERE id = 'error-uuid'
UPDATE http_error_logs SET ai_status = 'ai_fixed', ai_fixed_at = NOW(),
ai_fix_notes = 'description' WHERE id = 'error-uuid'
Good fix notes examples:
- "Added null check for user object in ProfileCard.tsx:45"
- "Fixed RLS policy for authenticated users on topics table"
- "Added missing CORS header to edge function"
Step 5: Report Summary
## Error Fix Summary
### JS Errors
#### Fixed (X)
- [error_type] in file.tsx:line - Brief description
#### Could Not Fix (X)
- [error_type] - Reason
### HTTP Errors
#### Fixed (X)
- [status_code] [method] /path - Brief description
#### Could Not Fix (X)
- [status_code] [method] /path - Reason
Step 6: Learn (invoke /opi)
After fixing errors, evaluate whether any fix revealed a non-obvious pattern worth capturing. Invoke the opi skill when:
- The root cause was surprising or non-obvious (e.g., table in wrong schema, MCP role limitations)
- The same mistake could easily happen again in new code
- A workaround was needed for a platform limitation
- The fix involved a multi-step diagnostic that future sessions should skip
How: Use the Skill tool to invoke opi with a summary of what was learned. The opi skill routes learnings to the right file (global LEARNINGS.md or skill-specific learnings.md).
Skip this step for straightforward fixes (typos, missing null checks, obvious import errors).
Database Schema Reference
ai_status: 'pending' | 'flagged_for_ai' | 'ai_fixed' | 'verified'
ai_prompt: text
ai_fixed_at: timestamp
ai_fix_notes: text
ai_status: 'pending' | 'flagged_for_ai' | 'ai_fixed' | 'verified'
ai_prompt: text
ai_fixed_at: timestamp
ai_fix_notes: text
get_errors_for_ai()
get_http_errors_for_ai()
mark_error_ai_fixed(p_id, p_fix_notes)
Best Practices
- Always read ai_prompt first - Admin may have specific instructions
- Don't fix what you don't understand - Mark as "could not fix"
- Run build after fixes - Verify no new errors
- Write clear fix notes - Help admin understand what changed
- Group related errors - Multiple errors may have same root cause
- Check Edge Function logs - For HTTP 500 errors, use Supabase MCP
get_logs
- For 400 RPC errors, always read the RPC source first -
SELECT prosrc FROM pg_proc WHERE proname = 'X'
- After remote RPC fix, create local migration -
supabase migration new <name>, write SQL, supabase migration repair <ts> --status applied
- Parse the PostgreSQL error code from response_body — it tells you exactly what went wrong (see references/learnings.md for code reference)