AI Chatbot 403-for-Self Bug - Debug Guide
Status: Documented for future debuggingDate: December 24, 2025
Priority: Medium (non-critical, but impacts user experience)
Symptom
Agent “Kenny Young” gets 403 Forbidden when asking about his own data via AI Assistant. Example Query:- Question: “how much did kenny young make this month”
- Endpoint:
POST https://payroll-backend-prod-evndxpcirq-uc.a.run.app/api/v1/ai/query-public - Response:
403 Forbidden - Expected:
200 OKwith commission data
- Request includes valid
Authorization: Bearer <token>header - Token is valid (not expired)
- User role:
agent - Requested target: “Kenny Young” (self)
Hypotheses
Hypothesis A: agent_id Enrichment Missing/None
Theory:current_user.agent_id is None because JWT → dim_users lookup fails.
Check:
- Cloud Run logs for
[RBAC-DEBUG]markers showingagent_id=None - Verify
get_user_by_email()is working - Verify
dim_userstable hasagent_idpopulated for Kenny’s email
api/routes/ai_query.pylines 3325-3337: JWT enrichment withget_user_by_email()
- Ensure
dim_userstable hasagent_idpopulated - Long-term: Embed
agent_idin JWT at login to avoid BigQuery dependency
Hypothesis B: Name→ID Resolution Mismatch
Theory:resolve_agent_name_to_id("Kenny Young") returns a different agent_id than current_user.agent_id.
Check:
- Cloud Run logs for
[RBAC]markers showing:requested_target: “Kenny Young”target_agent_id: resolved agent_iduser_agent_id: current_user.agent_id
- Compare resolved
target_agent_idvsuser_agent_id(should match)
api/utils/rbac.pylines 338-345:resolve_agent_name_to_id()callapi/utils/rbac.pylines 347-350: Self-check comparison
- Ensure name resolution uses exact match (case-insensitive, trimmed)
- Verify
dim_agent_hierarchyhas single active row for Kenny’s agent_id
Hypothesis C: Tenant ID Mismatch
Theory: JWTtenant_id doesn’t match request context tenant_id.
Check:
- Cloud Run logs for
[RBAC-DEBUG]markers showing:User tenant_id: from JWTRequested tenant_id: from request context
- Compare tenant_ids (should match)
api/utils/rbac.pylines 316-321: Tenant isolation check
- Ensure JWT contains correct
tenant_id - Ensure request context uses same
tenant_idas JWT
Hypothesis D: requested_target Extraction Issue
Theory: LLM extracts a different name than user’sagent_name (e.g., “Kenny” vs “Kenny Young”).
Check:
- Cloud Run logs for
[RBAC-DEBUG]markers showing:requested_target: extracted from questionUser agent_name: from JWT/enrichment
- Compare extracted name vs user’s agent_name
api/routes/ai_query.pyintent resolution: Extractsrequested_targetfrom questionapi/utils/rbac.pylines 332-336: Defaults to self if no target specified
- Ensure intent resolution extracts full name (not partial)
- Or: Default to self if extracted name matches user’s agent_name (fuzzy match)
Required Logs/Values to Capture
1. Request Start Logs
Look for[RBAC-DEBUG] ===== REQUEST START ===== in Cloud Run logs:
User agent_id: Should be non-null (if null → Hypothesis A)User agent_name: Should match extracted target (if different → Hypothesis D)Tenant ID: Should match request context (if different → Hypothesis C)
2. Intent Resolution Logs
Look for intent-specific logs (e.g.,[RBAC-DEBUG] AGENT_COMMISSION):
requested_target: Extracted agent name from questionauthorized_agent_id: Result ofauthorize_target_agent_id()(should be non-null for self)
3. RBAC Authorization Logs
Look for[RBAC] markers in authorize_target_agent_id():
- Self-check result: Should allow self access
- Downline check result: Should allow if target is self
4. Name Resolution Logs
Look for[RBAC] markers in resolve_agent_name_to_id():
Resolved agent_id: Should matchuser_agent_id(if different → Hypothesis B)
Minimal Reproduction Checklist
Step 1: Reproduce the Bug
- Login as Kenny Young (agent role)
- Open AI Assistant in dashboard
- Send Query: “how much did kenny young make this month”
- Observe Response: Should get 403 Forbidden (bug)
Step 2: Capture Logs
-
Open Cloud Run Logs:
-
Extract Key Values:
current_user.agent_idcurrent_user.agent_namecurrent_user.tenant_idrequested_target(extracted from question)target_agent_id(resolved from name)authorized_agent_id(result of authorization)
Step 3: Compare Values
-
Compare agent_ids:
current_user.agent_idvstarget_agent_id(should match)- If different → Hypothesis B
-
Compare tenant_ids:
current_user.tenant_idvs request contexttenant_id(should match)- If different → Hypothesis C
-
Check agent_id enrichment:
current_user.agent_idis null → Hypothesis Acurrent_user.agent_idis non-null → Check other hypotheses
-
Check name extraction:
requested_targetvscurrent_user.agent_name(should match or be similar)- If very different → Hypothesis D
Step 4: Verify Data
-
Check dim_users table:
- Verify
agent_idis populated - Verify
agent_namematches expected
- Verify
-
Check dim_agent_hierarchy table:
- Verify single active row exists
- Verify
agent_idmatchesdim_users.agent_id
Debugging Commands
1. Query Cloud Run Logs
2. Test Name Resolution
3. Test Downline Check
Expected Fixes
If Hypothesis A (agent_id enrichment missing)
Fix: Ensuredim_users table has agent_id populated, or embed agent_id in JWT at login.
Code Changes:
api/routes/auth.py(or login endpoint): Addagent_idto JWT payload- Or: Fix
get_user_by_email()to returnagent_id
If Hypothesis B (name→id resolution mismatch)
Fix: Ensure name resolution uses exact match and returns correctagent_id.
Code Changes:
api/utils/rbac.pyresolve_agent_name_to_id(): Verify exact match logic- Or: Use fuzzy matching to handle name variations
If Hypothesis C (tenant_id mismatch)
Fix: Ensure JWT and request context use sametenant_id.
Code Changes:
api/routes/ai_query.py: Verifytenant_idextraction from JWT matches request context
If Hypothesis D (requested_target extraction issue)
Fix: Default to self if extracted name matches user’s agent_name (fuzzy match). Code Changes:api/utils/rbac.pyauthorize_target_agent_id(): Add fuzzy name matching for self-check
Related Documentation
docs/AI_CHATBOT_INCIDENT_2025_12_24.md- Complete incident summarydocs/AI_CHATBOT_SECURITY.md- Security features and API contractdocs/DEPLOYMENT_RUNBOOK.md- Deployment proceduresapi/utils/rbac.py- RBAC implementationapi/routes/ai_query.py- AI query endpoint implementation