Common Issues
Known issues, gotchas, and their fixes. These are problems the team has encountered before -- check here first when something breaks.
Database Column Name Mismatches
seniority vs seniority_level
Problem: Code references seniority_level but the column does not exist.
Fix: The attorneys table uses seniority (not seniority_level). Update any queries or code to use the correct column name.
-- Correct
SELECT seniority FROM attorneys WHERE id = '...';
-- Wrong -- will throw "column does not exist"
SELECT seniority_level FROM attorneys WHERE id = '...';
email_status Enum Values
Problem: Inserting a row with email_status = 'unverified' fails with an enum validation error.
Fix: The email_status column only accepts these four values:
| Allowed Values |
|---|
valid |
invalid |
catch_all |
unknown |
There is no unverified value. Use unknown instead if the status has not been determined.
disposition is TEXT, Not Enum
Problem: Code tries to cast or validate disposition against an enum type that no longer exists.
Fix: The calls.disposition column was migrated from the call_disposition enum to plain TEXT in migration 20260210200001_convert_disposition_to_text.sql. It now accepts any string, but should always be set to one of the 9 canonical dispositions in Title Case:
- No Answer
- Voicemail
- Gatekeeper
- Incorrect Number
- Not Interested
- Callback Requested
- Future Potential
- Meeting Booked
- Do Not Contact
Type System Issues
attorneys Table Not in Auto-Generated Types
Problem: TypeScript types for the attorneys table are missing or stale after regenerating Supabase types.
Cause: The attorneys table was created via the Supabase dashboard, NOT through migration files. The Supabase type generator only picks up tables defined in migrations.
Fix: Types for the attorneys table are manually maintained in src/integrations/supabase/types.ts. After regenerating types, you must manually re-add the attorneys table definition. The full interface is also defined in src/lib/attorney-utils.ts.
Sales Types Not in Supabase Types
Problem: TypeScript types for sales_prospects, sales_calls, or sales_meetings are not found in Supabase types.
Fix: Sales-specific types are defined in src/lib/sales-types.ts, not in the Supabase types file. Import from the correct location:
// Correct
import type { SalesProspect, SalesCall } from '@/lib/sales-types';
// Wrong -- may not exist in Supabase types
import type { Database } from '@/integrations/supabase/types';
Blitz API Issues
422 Errors from Email/Phone Endpoints
Problem: Blitz API returns HTTP 422 (Unprocessable Entity) when calling email or phone enrichment.
Cause: Using the wrong field name. The v2 API requires person_linkedin_url, but older code may use linkedin_profile_url.
Fix:
// Correct (v2)
const payload = { person_linkedin_url: 'https://linkedin.com/in/...' };
// Wrong -- causes 422
const payload = { linkedin_profile_url: 'https://linkedin.com/in/...' };
GHL Issues
Mixed Case callStatus
Problem: GHL callStatus comparisons fail because values are mixed case.
Cause: GHL sends callStatus in inconsistent casing: Answered, No answer, completed, Busy, etc.
Fix: Always normalize to lowercase before comparing:
const status = payload.callStatus?.toLowerCase();
if (status === 'answered' || status === 'completed') {
// Someone picked up
}
Duplicate Call Records
Problem: The same call appears twice in the database.
Cause: GHL fires 2 webhook hits per call (Call Completed + Disposition workflows).
Fix: Always dedup on external_call_id before inserting:
SELECT id FROM sales_calls WHERE external_call_id = $1 LIMIT 1;
If a record exists, skip the insert and return 200 to acknowledge the webhook.
Calendar Issues
Wrong Calendar Table
Problem: Calendar connection queries return empty results even though the user has connected their calendar.
Cause: Client mode and internal mode use different tables.
Fix:
| Mode | Table |
|---|---|
| Client bookings | calendar_connections |
| Internal (user) bookings | user_calendar_connections |
Check which mode the booking link uses:
const isInternalLink = !bookingLink?.client_id && !!bookingLink?.user_id;
Then query the correct table.
OAuth State Parsing
Problem: OAuth callback fails or redirects to the wrong page.
Cause: The state parameter format differs between client and internal modes.
| Mode | State Format | Redirect After OAuth |
|---|---|---|
| Client | {clientId}:{provider} | /workspace/{clientId}/client-info |
| Internal | user:{userId}:{provider} | /admin/sales/scheduling |
Ensure the callback handler correctly parses both formats.
Build and Deployment Issues
Vite Build Fails with Type Errors
Problem: npm run build fails with TypeScript errors that do not appear in the dev server.
Cause: Vite's dev server is more lenient than the production build. The build runs tsc in strict mode.
Fix: Run npm run lint and fix all TypeScript errors before building.
Edge Function Returns 401
Problem: An edge function returns 401 Unauthorized even with a valid request.
Cause: JWT verification is enabled by default. Webhook endpoints from external services cannot send JWTs.
Fix: Set verify_jwt = false in supabase/config.toml for the affected function, then redeploy:
[functions.ghl-call-webhook]
verify_jwt = false
supabase functions deploy ghl-call-webhook