Skip to main content

CRM troubleshooting

A symptom → cause → fix table for the most common dev + ops issues. Bookmark this when something is "weird". See security.md for the broader gate model when 4xx/5xx looks wrong.

Authentication & permissions

SymptomCauseFix
401 on every requestidentity backend unreachable from CRMcurl ${IDENTITY_URL}/api/user -H "Authorization: Bearer $TOKEN" — should return 200
503 identity_unavailableidentity is up but slow / 5xxwait — CRM auto-recovers within 60s (user cache TTL); SPA should NOT log out on this status
403 tenant_not_a_membertoken's tenants[] doesn't include the bound tenantre-mint token; verify TENANT_ID matches one of the user's tenants in identity
401 token_shape_legacytoken predates F-future-6 + 2026-06-15 cutoff hitre-authenticate — old token shape no longer accepted
403 service_account_scope_deniedservice-account token's scopes[] doesn't cover the requested permissionadd the scope at the identity-side API key; see service-accounts
403 permission_denieduser's policies don't grant the verbcheck identity-side role mapping; php artisan crm:list-permissions --fqn lists what CRM expects
403 no_policiesidentity returned an empty policies arrayidentity-side role missing? CheckPermission's policy fetch returned []

File upload / download

SymptomCauseFix
403 File path rejected. on chat file servestored path contains .. (legacy row) or doesn't start with the channel prefixDB hygiene — clean the metadata.path field
422 version_chain_size_exceededdocument version chain hit 1 GiB totaldelete older versions of that document
422 mimetypes validation on media uploadclient sent an HTML/SVG with a .png extensionuse a real image; the whitelist is jpeg/png/gif/webp/avif/pdf
Filename in DB has bin extensionoriginal extension didn't match [a-z0-9]{1,8} regexthis is correct — the regex protects against path-traversal
Document update 403 even though user has folder writesource AND target folder write checked on move (KRITIKUS #4)user must have write on both folders, not just the destination

Data layer

SymptomCauseFix
ProjectMember re-add 422 "already a member"soft-deleted tombstone existsthe add handler restores the tombstone; if it's claiming "active", check deleted_at in DB
task.position collisions on the boardtwo concurrent creates raced before the lockForUpdate fixre-run the create — DB::transaction + Project::lockForUpdate now serialises
Search returns nothing for "Árvai"unaccent extension not loadedCREATE EXTENSION IF NOT EXISTS unaccent (the migration does this; rerun migrate if you skipped it)
Search works for "arvai" but matches "Árva" tooworking as designed — unaccent matches both
SLA breach duplicateconcurrent CheckSlaBreachesJob runs raced past the partial unique indexcheck the partial unique index sla_breaches_unresolved_unique exists; rerun migration if missing

Identity event consumer

SymptomCauseFix
crm.identity-events.{tenant} queue not drainingworker isn't runningphp artisan crm:consume-identity-events &
TenantUser row missing for a user that logged in oncelazy-create not running, identity backend not emitting identity.tenant.member_addedcheck identity logs; the IdentityEventHandler is a logger-only on member_added — TenantUser is created by IdentityService::getUserFromToken on first call
Policy cache stale after identity-side role changeidentity.policy.updated event not consumed (queue drained?)check crm.identity-events.{tenant} is bound and consumed; on file/database cache driver, fall back is 1h TTL
removed_at column reference in IdentityEventHandlerlegacy code — already fixed (batch 1 menet 4)pull latest

Frontend

SymptomCauseFix
Dashboard cURL error 7: localhost:8080DevOps API unreachableDEVOPS_API_MOCK=true in main-backend/.env; restart make dev-admin-backend
Login redirects loop SPA → identity → SPA → …OAuth client mismatchsync OAUTH_CLIENT_ID + OAUTH_CLIENT_SECRET across identity seeder + main-backend .env
Failed to resolve import @layouts/plugins/caslVuexy template ref to deleted pluginshim already in place at src/@layouts/plugins/casl.ts
:invalid_client 401 on token exchangesecret mismatchmake fresh + sync env (Passport seeder regenerates the secret each fresh)
Network 422 with no detailed error displayedstore didn't call notifyError(err, 'fallbackKey')retrofit the store to use useApiError (see existing stores for pattern)
Pagination "page=3" in URL but list always opens on page 1usePaginationDeepLink not adopted on that pageapply the composable (see companies/contacts/projects/kb-articles refs)

Identity ↔ CRM SDK drift

SymptomCauseFix
TS compile error in store after backend changeregenerated SDK missing locallynpm run api:sync
CI fails on npm run api:checkgenerated SDK is stalerun npm run api:sync and commit the diff
Identity PolicySeeder rejects a CRM permission as unknownidentity-side PermissionRegistry doesn't have it yetadd to PermissionRegistry + reseed; crm:list-permissions --fqn --json is the canonical source

Tests

SymptomCauseFix
vitest command not founddev deps not installednpm i -D vitest @vitest/ui happy-dom @vue/test-utils
PHPUnit can't find Carbon::now() in unit testunit tests don't bootstrap the Laravel containeruse Feature TestCase if you need facades / DB; pure model tests work without it
Test passes locally but fails on CIenv-dependent (timezone, locale, DB driver)wrap time-dependent assertions in Carbon::setTestNow(); tests that touch DB should declare RefreshDatabase

Migration / fresh

SymptomCauseFix
audit_logs has no column updated_atseeder explicitly passed updated_at to a $timestamps = false modelalready fixed (batch 1 follow-up)
relation "project_members_project_id_user_id_unique" does not existpartial unique index migration not runphp artisan migrate — covers it
extension "unaccent" does not existsuperuser-only extension can't be loaded by the migration usergrant the migration role CREATEROLE + CREATEDB, or run CREATE EXTENSION unaccent as superuser before migrate

Performance

SymptomCauseFix
/api/user round-trip on every paginated XHRpost-2026-05-12 there's a 60s token-hash cache; if cold, expectedfirst request warms it; subsequent within TTL are cache hits
Policy fetch on every requestcache key includes a token-hash suffix (cross-repo #39); each new token mints a new slotexpected behavior — keeps stale-after-rotation impossible at cost of 1 fetch per token-rotation
crm:list-permissions 30+ secondsroute count is large but no DB hit — should be sub-secondphp artisan route:cache may have stale cache; clear with php artisan route:clear

When all else fails

  1. tail -f tenant/tenant-backend-crm/storage/logs/laravel.log — server-side errors
  2. Browser devtools Network tab — request/response shapes
  3. php artisan crm:list-permissions — what CRM expects to be in identity's PolicySeeder
  4. RabbitMQ management UI — is the per-tenant queue bound + draining?
  5. tenant/tenant-backend-crm/AUDIT_FINDINGS.md — has this case already been addressed?