Files
rehearshalhub/ERROR_ANALYSIS.md
Mistral Vibe b72cdf0bd3 Add detailed error analysis for 403 issues
- Identified root cause: list_invites endpoint requires admin role
- Should allow regular members to see invites
- Found bug in bands.py line 33
- Includes recommended fixes and action plan

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-04-01 12:33:15 +02:00

5.6 KiB

403 Error Analysis - Invited Users Cannot Access Band Resources

🚨 CRITICAL ISSUE IDENTIFIED

The Problem

Invited users are getting 403 Forbidden errors when trying to:

  1. Access band invites: GET /api/v1/bands/{band_id}/invites
  2. Stream audio versions: GET /api/v1/versions/{version_id}/stream

Root Cause Found

🔍 Code Investigation Results

1. Invite Acceptance Flow ( WORKING)

File: api/src/rehearsalhub/routers/members.py (lines 86-120)

@router.post("/invites/{token}/accept", response_model=BandMemberRead)
async def accept_invite(token: str, ...):
    # 1. Get invite by token
    invite = await repo.get_invite_by_token(token)
    
    # 2. Validate invite (not used, not expired)
    if invite.used_at: raise 409
    if invite.expires_at < now: raise 410
    
    # 3. Check if already member (idempotent)
    existing_role = await repo.get_member_role(invite.band_id, current_member.id)
    if existing_role: raise 409
    
    # 4. ✅ Add member to band (THIS WORKS)
    bm = await repo.add_member(invite.band_id, current_member.id, role=invite.role)
    
    # 5. ✅ Mark invite as used (THIS WORKS)
    invite.used_at = datetime.now(timezone.utc)
    invite.used_by = current_member.id
    
    return BandMemberRead(...)

The invite acceptance logic is CORRECT and should work!

2. Band Invites Endpoint ( PROBLEM FOUND)

File: api/src/rehearsalhub/routers/bands.py (lines 19-70)

@router.get("/{band_id}/invites", response_model=BandInviteList)
async def list_invites(band_id: uuid.UUID, ...):
    # ❌ PROBLEM: Only ADMINS can list invites!
    role = await repo.get_member_role(band_id, current_member.id)
    if role != "admin":  # ← THIS IS THE BUG!
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Admin role required to manage invites"
        )
    
    # Get invites...

BUG FOUND: The /bands/{band_id}/invites endpoint requires ADMIN role!

But regular members should be able to see invites for bands they're in!

3. Audio Stream Endpoint ( PROBLEM FOUND)

File: api/src/rehearsalhub/routers/versions.py (lines 208-215)

async def _get_version_and_assert_band_membership(version_id, session, current_member):
    # ... get version and song ...
    
    # ❌ PROBLEM: Uses assert_membership which should work
    band_svc = BandService(session)
    try:
        await band_svc.assert_membership(song.band_id, current_member.id)
    except PermissionError:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a member")

BUG FOUND: The /versions/{version_id}/stream endpoint uses assert_membership which should work for regular members.

But if the user wasn't properly added to band_members, this will fail!

🎯 THE ROOT CAUSE

Hypothesis 1: Invite Acceptance Failed

  • User accepted invite but wasn't added to band_members
  • Need to check database

Hypothesis 2: Permission Logic Too Strict

  • /bands/{id}/invites requires admin (should allow members)
  • This is definitely a bug

Hypothesis 3: JWT Token Issue

  • User's JWT doesn't reflect their new membership
  • Token needs to be refreshed after invite acceptance

CONFIRMED BUGS

Bug #1: List Invites Requires Admin (SHOULD BE MEMBER)

File: api/src/rehearsalhub/routers/bands.py:33

# CURRENT (WRONG):
if role != "admin":
    raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Admin role required")

# FIXED (CORRECT):
if role is None:
    raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Not a member")

Bug #2: Invite Acceptance Might Not Work

Need to verify:

  1. Database shows user in band_members
  2. JWT token was refreshed
  3. No errors in invite acceptance flow

Fix #1: Change Permission for List Invites

# In api/src/rehearsalhub/routers/bands.py
async def list_invites(band_id: uuid.UUID, ...):
    # Change from admin-only to member-only
    role = await repo.get_member_role(band_id, current_member.id)
    if role is None:  # ← Changed from != "admin"
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Not a member of this band"
        )

Fix #2: Verify Invite Acceptance

-- Check if user is in band_members
SELECT * FROM band_members 
WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383'
AND member_id = '{user_id}';

-- Check invite status
SELECT * FROM band_invites 
WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383'
AND used_by = '{user_id}';

Fix #3: Add Debug Logging

# In accept_invite endpoint
log.info(f"User {current_member.id} accepting invite to band {invite.band_id}")
log.info(f"Adding member with role: {invite.role}")
log.info(f"Invite marked as used at {datetime.now(timezone.utc)}")

📋 ACTION PLAN

Step 1: Fix List Invites Permission

  • Change role != "admin" to role is None
  • Test with regular member account

Step 2: Verify Database State

  • Check band_members table
  • Check band_invites table
  • Verify user was added correctly

Step 3: Test Invite Flow

  • Create new invite
  • Accept as test user
  • Verify user can access band resources

Step 4: Deploy Fix

  • Apply permission fix
  • Add logging
  • Monitor for issues

🎯 IMPACT

Current: Invited users cannot access band resources (403 errors) After Fix: Regular band members can see invites and access recordings

Files to Change:

  • api/src/rehearsalhub/routers/bands.py (line 33)

Estimated Time: 15-30 minutes to fix and test