FKITDEV-8787 / SLARAFIPI-60 — Raiffeisen Myra “Already authorized” / “Already has some kind of room”
One-line summary
Three Raiffeisen Myra mobile self-service rooms (186120, 186126, 186135) failed in production. Two of the three customer logs show the strings
"Already authorized"and"Already has some kind of room". Triage finds these strings live in the FaceKom mobile SDK, not in vuer_oss — server-sideselfService:v2:startsilently resumes any non-closed room. Working hypothesis: in-app Restart button leaves stale SDK session state. Class is the same phantom-room family as FKITDEV-4736 (instrumentation, 2024-09) and same flow surface as the SLARAFIPI-53 / FKITDEV-7667 fix (2025-10).
Ticket chain
- FKITDEV-8787 (FaceKom Dev) — triaged 2026-05-13.
- SLARAFIPI-60 (Raiffeisen SLA) — partner-tagged Minor; upgraded to P2-Medium here (customer-blocking, no in-app recovery path).
- Related prior tickets (no Obsidian notes yet): SLARAFIPI-53 / FKITDEV-7667 (2025-10 flow fix, author
m3szi, commitf830fd8e5a); FKITDEV-4736 (phantom-room instrumentation, commit2a53553577, 2024-09-30).
Symptoms
- Three SelfServiceRooms on Raiffeisen Myra mobile self-service: 186120, 186126, 186135.
- Customer of room 186120 has a vestigial duplicate (
csökevény szoba) in 186126 — samecustomerId. - Customer of room 186135 has no sibling room.
- 186126 customer never received tasks on the phone, retried 5x; after restart couldn’t pass the first photo.
- Both 186126 and 186135 logs contain the strings
"Already authorized"and"Already has some kind of room". - Partner (Raiffeisen) suspects the in-app “Restart” button leaves stale SDK memory.
Triage outcome
- Severity: P2-Medium. Customer-blocking on the bank’s primary self-service flow with no in-app recovery — upgrades partner-tagged Minor.
- Class: phantom-room family. Same surface as FKITDEV-4736 (instrumentation) and SLARAFIPI-53 / FKITDEV-7667 (2025-10 fix).
- State: triaged; blocked on Raiffeisen for mobile-SDK confirmation.
Where the error strings come from
The error strings are not in
vuer_ossVerified by
git grepon bothdevelandcustomization/raiffeisen: the literals"Already authorized"and"Already has some kind of room"do not appear in the OSS server source. They originate in the FaceKom mobile SDK (Android/iOS) local guard layer.
Best reading of the SDK guards:
Already authorized— SDK already holds a customer JWT in memory; refusing to re-register.Already has some kind of room— SDK already holds aselfServiceRoomData.idcached locally; refusing to start a new room.
Server side has no equivalent throw on V2. SelfServiceV2Service.start() silently resumes:
{ created: false, selfServiceRoomId: openRoom.id, currentStep }V1 does throw at SelfServiceRoomService.js:217 (Customer already has an open room), but SelfServiceActions.js:27-34 swallows it into a generic { success: false } — so even the V1 error never surfaces to the SDK as a rich code.
Csökevény szoba mechanism (by design)
By-design, but a debugging trap
customization/raiffeisen/server/service/RaiffeisenService.js::resolveExternalToken()returns the samecustomer.idfor the sameofferId. When the bank reissues the link or the customer gets a new attempt,SelfServiceV2Service.start()sees the previous SSR isclosedand creates a new SSR for the samecustomerId. The previous closed SSR is the “vestigial room” (csökevény). Intentional, but it makes the duplicate-room arrangement look like a bug to anyone reading the DB cold.
Suspected race that produces the rarer non-closed duplicate:
- Two
start()calls in quick succession on a flapping SDK both pass the_findOpenRoomForCustomernull check. - Result: two SSRs for the same
customerIdin non-closed states. - Nothing at the DB layer guards against this — no partial unique index on
(customerId)for('waiting','incall','left').
Working hypothesis (root-cause path)
Hypothesis pending Raiffeisen confirmation
The mobile-SDK angle is the leading hypothesis. It is consistent with all observable evidence (especially the two error strings being SDK-local), but only the Raiffeisen mobile team can confirm what
Restartdoes to SDK state. Treat as hypothesis until they answer.
Working hypothesis sequence
sequenceDiagram participant C as Customer (Myra app) participant SDK as FaceKom SDK participant OSS as vuer_oss server participant DB as SelfServiceRoom C->>SDK: Start KYC SDK->>OSS: register / start OSS->>DB: create SSR (waiting) OSS-->>SDK: {created:true, ssrId, currentStep} Note over C,SDK: Flow stalls (network or missing task) C->>SDK: Tap in-app Restart Note over SDK: clearSession() NOT called<br/>JWT + ssrData still in memory SDK->>SDK: register guard hits → 'Already authorized' SDK->>SDK: start guard hits → 'Already has some kind of room' Note over OSS: never receives a fresh request Note over DB: SSR still waiting/incall/left → no new tasks dispatched Note over C: 'no tasks on phone' OSS->>DB: timeout closes original SSR Note over C: bank reissues link → new SSR for same customerId C->>SDK: New attempt SDK-->>C: stale state still refuses re-init → couldn't pass first photo
- Mobile flow stalls (intermittent connectivity or missing task).
- Customer taps in-app Restart instead of OS-killing the app.
- SDK retains in-memory
selfServiceRoomData+ customer JWT — noclearSession(). - SDK calls
register/startagain → SDK local guard short-circuits with the two error strings without contacting OSS. - OSS still has the previous SSR in
waiting/incall/left→ no new tasks dispatched → “no tasks” symptom. - Eventually OSS-side timeout closes the SSR → bank reissues → new SSR (e.g. 186126) created for same
customerId→ previous SSR (186120) becomescsökevény. - After the in-app restart the SDK’s stale state still refuses to re-init → “couldn’t pass first photo”.
Code surfaces (file:line)
| Layer | Location | Note |
|---|---|---|
| OSS V2 service | server/service/SelfServiceV2Service.js:357-394 | start() silently resumes any non-closed room |
| OSS V2 service | server/service/SelfServiceV2Service.js:224-232 | _findOpenRoomForCustomer (used by V2) |
| OSS V1 service | server/service/SelfServiceRoomService.js:213-228 | V1 throw at line 217: Customer already has an open room |
| OSS V1 service | server/service/SelfServiceRoomService.js:357-389 | V1 _findOpenRoomForCustomer |
| OSS RPC | server/queue/rpc_server/SelfServiceActions.js:27-34 | V1 RPC handler swallows the V1 throw into { success: false } |
| OSS RPC | server/queue/rpc_server/SelfServiceV2.js:49-54 | register mints JWT carrying only { customerId }, no selfServiceRoomId |
| OSS DB | server/db/model/selfserviceroom.js:3 | status enum: ['waiting','incall','left','closed','deleted','archived'] — only the last three are treated as “not open” |
| OSS cron | server/cron/CloseExpiredSelfServiceRoomsCronJob.js:20 | only closes by expireAt; does not reap stuck waiting / left / incall |
| Raiffeisen | customization/raiffeisen/server/service/SelfServiceV2Service.js | has PRDEBUG instrumentation gated by raiffeisen.debug.phantomRoomLog (added FKITDEV-4736, commit 2a53553577, 2024-09-30) |
| Raiffeisen | customization/raiffeisen/customization/listeners/self-service-v2.js | self-service:v2:create hook unconditionally creates the SSR row — no abort-prior-open sweep |
| Raiffeisen | customization/raiffeisen/customization/flow/myra-self-service-v2-phase-1/myra-self-service-v2-phase-1.flow.handler.js:10-75 | sets identificationStatus='room', aborts associated Flow on room failure |
| Raiffeisen | customization/raiffeisen/server/service/RaiffeisenService.js:1066-1087 | resolveExternalToken() returns existing customer.id for same offerId — mechanism behind the csökevény arrangement |
Open questions / data needed from Raiffeisen
Blocked on Raiffeisen data
The hypothesis cannot be confirmed without mobile-side artifacts. Items below are the asks for the partner.
- Mobile SDK version + which file/symbol emits each error string.
- Does the in-app “Restart” call SDK
clearSession()or just re-run the mobile flow? - JWT decoded
offerIdfor 186120 vs 186126 vs 186135 — does the sharedcustomerIdcome from the sameofferId? - Mobile-side log timestamps for the two error strings, correlated with server-side
selfService:v2:register/:startcalls. - Confirm
raiffeisen.debug.phantomRoomLogistruein PROD config. - Is
OneDrive_1_5-5-2026.zip(attachment on the ticket) the mobile log bundle? — almost certainly yes.
Recommended fixes (ordered)
| Order | Type | Action |
|---|---|---|
| D | Operational, immediate | Enable raiffeisen.debug.phantomRoomLog: true in PROD; ask Raiffeisen to repro |
| C | Mobile, primary | In-app Restart must call selfService:v2:abort then clearSession() before re-starting |
| A | Server, defense in depth | In SelfServiceV2Service.start(), when _findOpenRoomForCustomer returns a room whose Flow is failed/aborted/cancelled or serviceProgress === 'preparation' and createdAt older than N minutes — abort+close it and create fresh |
| B | Server, long-term | Partial unique index on (customerId) WHERE status IN ('waiting','incall','left') on selfServiceRoom, with cleanup migration |
| E | Runbook | SQL to manually close a vestigial blocking room: UPDATE "selfServiceRoom" SET status='closed', "closedAt"=now() WHERE id=<X> — for support |
| F | Server, error UX | Stop swallowing the V1 throw in SelfServiceActions.js:31-34; return the actual error code |
Escalation path
- Ask reporter for mobile app version + the data items in Open questions.
- Enable
phantomRoomLogin prod. - Coordinate with m3szi (author of
f830fd8e5a/ FKITDEV-7667) — owns the same flow handler. - If mobile fix needed, ticket the mobile team (Raiffeisen Myra app).
- Server fixes target raiffeisen first, forward-port to
devel.
Cross-references
- SLARAFIPI-53 / FKITDEV-8581 / FKITDEV-7667 — same flow, prior fix (no Obsidian note yet).
- FKITDEV-4736 — phantom-room instrumentation (commit
2a53553577). - Original RCA file (already on disk):
/Users/levander/coding/facekom/SLARAFIPI-53-osszefoglalo.md.
Related
- FaceKom
- vuer_oss
- customization-branches — Raiffeisen overrides on
SelfServiceRoomService.jsandSelfServiceV2Service.js - breakage-risks — overlapping core/Raiffeisen-override surfaces
- debug-agents
- release-process