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-side selfService:v2:start silently 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, commit f830fd8e5a); FKITDEV-4736 (phantom-room instrumentation, commit 2a53553577, 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 — same customerId.
  • 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_oss

Verified by git grep on both devel and customization/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 a selfServiceRoomData.id cached 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 same customer.id for the same offerId. When the bank reissues the link or the customer gets a new attempt, SelfServiceV2Service.start() sees the previous SSR is closed and creates a new SSR for the same customerId. 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 _findOpenRoomForCustomer null check.
  • Result: two SSRs for the same customerId in 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 Restart does to SDK state. Treat as hypothesis until they answer.

  1. Mobile flow stalls (intermittent connectivity or missing task).
  2. Customer taps in-app Restart instead of OS-killing the app.
  3. SDK retains in-memory selfServiceRoomData + customer JWT — no clearSession().
  4. SDK calls register / start again → SDK local guard short-circuits with the two error strings without contacting OSS.
  5. OSS still has the previous SSR in waiting / incall / left → no new tasks dispatched → “no tasks” symptom.
  6. Eventually OSS-side timeout closes the SSR → bank reissues → new SSR (e.g. 186126) created for same customerId → previous SSR (186120) becomes csökevény.
  7. After the in-app restart the SDK’s stale state still refuses to re-init → “couldn’t pass first photo”.

Code surfaces (file:line)

LayerLocationNote
OSS V2 serviceserver/service/SelfServiceV2Service.js:357-394start() silently resumes any non-closed room
OSS V2 serviceserver/service/SelfServiceV2Service.js:224-232_findOpenRoomForCustomer (used by V2)
OSS V1 serviceserver/service/SelfServiceRoomService.js:213-228V1 throw at line 217: Customer already has an open room
OSS V1 serviceserver/service/SelfServiceRoomService.js:357-389V1 _findOpenRoomForCustomer
OSS RPCserver/queue/rpc_server/SelfServiceActions.js:27-34V1 RPC handler swallows the V1 throw into { success: false }
OSS RPCserver/queue/rpc_server/SelfServiceV2.js:49-54register mints JWT carrying only { customerId }, no selfServiceRoomId
OSS DBserver/db/model/selfserviceroom.js:3status enum: ['waiting','incall','left','closed','deleted','archived'] — only the last three are treated as “not open”
OSS cronserver/cron/CloseExpiredSelfServiceRoomsCronJob.js:20only closes by expireAt; does not reap stuck waiting / left / incall
Raiffeisencustomization/raiffeisen/server/service/SelfServiceV2Service.jshas PRDEBUG instrumentation gated by raiffeisen.debug.phantomRoomLog (added FKITDEV-4736, commit 2a53553577, 2024-09-30)
Raiffeisencustomization/raiffeisen/customization/listeners/self-service-v2.jsself-service:v2:create hook unconditionally creates the SSR row — no abort-prior-open sweep
Raiffeisencustomization/raiffeisen/customization/flow/myra-self-service-v2-phase-1/myra-self-service-v2-phase-1.flow.handler.js:10-75sets identificationStatus='room', aborts associated Flow on room failure
Raiffeisencustomization/raiffeisen/server/service/RaiffeisenService.js:1066-1087resolveExternalToken() 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 offerId for 186120 vs 186126 vs 186135 — does the shared customerId come from the same offerId?
  • Mobile-side log timestamps for the two error strings, correlated with server-side selfService:v2:register / :start calls.
  • Confirm raiffeisen.debug.phantomRoomLog is true in PROD config.
  • Is OneDrive_1_5-5-2026.zip (attachment on the ticket) the mobile log bundle? — almost certainly yes.
OrderTypeAction
DOperational, immediateEnable raiffeisen.debug.phantomRoomLog: true in PROD; ask Raiffeisen to repro
CMobile, primaryIn-app Restart must call selfService:v2:abort then clearSession() before re-starting
AServer, defense in depthIn 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
BServer, long-termPartial unique index on (customerId) WHERE status IN ('waiting','incall','left') on selfServiceRoom, with cleanup migration
ERunbookSQL to manually close a vestigial blocking room: UPDATE "selfServiceRoom" SET status='closed', "closedAt"=now() WHERE id=<X> — for support
FServer, error UXStop swallowing the V1 throw in SelfServiceActions.js:31-34; return the actual error code

Escalation path

  1. Ask reporter for mobile app version + the data items in Open questions.
  2. Enable phantomRoomLog in prod.
  3. Coordinate with m3szi (author of f830fd8e5a / FKITDEV-7667) — owns the same flow handler.
  4. If mobile fix needed, ticket the mobile team (Raiffeisen Myra app).
  5. 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.