A Hungarian ID back side (MRZ TD1, classId 5, HUN-BO-06001_BACK_PO) reads inconsistently. The full back-side image scores MRZ valid_score: 100, but the warped/cropped image of the same capture scores 2 with garbage MRZ fields — and the self-service flow rejects the registration because it evaluates the cropped result. SLARAFIPI-61 was prematurely marked “Solved” after a vuer_cv4.9.0 test scored 100, but that test almost certainly ran on the FULL image, not the crop. This is a triage, not a resolved RCA.
This is a TRIAGE, not an RCA
No root cause is confirmed. The investigation hinges on one decisive experiment (checklist #2(b)). Status will move to “Root Cause Identified” only after CV 4.9.0 MRZ is run on the CROPPED image from the 191549 export.
Ticket chain
SLARAFIPI-61 (SLA Raiffeisen PION, Production) — was marked “Solved”, reopened.
FKITDEV-8788 (FaceKom Dev) — Open, reassigned to andras.lederer 2026-05-19.
Sibling:FKITDEV-8787 / SLARAFIPI-60 — same client family (raiffeisen), different surface (phantom room).
The open question that reopened the ticket
The unanswered question
Does CV 4.9.0 fix the cropped-image MRZ case, or only the full-image case? SLARAFIPI-61 was closed on a 4.9.0 test that scored 100 — but that score was almost certainly produced on the FULL image, which already passed pre-4.9.0. The production rejection path evaluates the crop, which scored 2.
Counterintuitively a visually “worse” photo (room 191541) passed while a “better” photo (room 191549) failed — pointing at a corner-detection / aspect-ratio edge case in the warp, not raw sharpness.
Severity: P2 (borderline P1)
Production onboarding fails for a whole document class (HU eID back, TD1) on some captures; no citizen-facing workaround; but intermittent, operators have a manual fallback (full-image MRZ works), no data loss.
Escalate to P1 if…
the HU-eID self-service rejection rate at Raiffeisen is high.
Affected components
vuer_cv (Python, remote / SSHFS — not in local checkout)
Surface
Location
Note
MRZ reader
engines/mrz_processor.py (behind POST /api/v1/mrz)
why 100 on full, 2 on crop?
Document warp/crop
warp engine behind POST /api/v2/document-warp
bad corner fit / over-tight crop can clip the bottom MRZ band
getMrzCheckResult — turns recognitionAttempts[0].mrz.valid into accept/reject
Supporting
server/.../RecognitionService.js:135,258-278
Supporting
server/.../VuerCVService.js:281-303,347-375
Supporting
VuerCVMRZDetector.js
Supporting
server/flow/FlowService.js:2976-3035
For Agents — config flags to check
ocr.engine (warp vs no-warp), ocr.useCVMRZData, hosts.cv. Check the raiffeisen branch for overrides + the recognition recipe (customization/cv/instruction.index).
Debugging checklist
Unpack selfserviceroom-export-191549.zip (read-only). Find the 3 images: (0) half-success, (1) FULL back side, (2) cropped. Confirm full→100, cropped→2 in stored RecognitionAttempts.
*** DECISIVE *** Against CV 4.9.0, image-upload + /api/v1/mrz on EACH: (a) FULL → expect 100; (b) CROPPED #2 → the unanswered question. If (b) still scores 2, 4.9.0 does NOT fix the production path → SLARAFIPI-61 closed wrongly.
Reproduce prod pipeline: /api/v2/document-warp on FULL image → document.imageId → /api/v1/mrz on THAT. If warp→mrz=2 while direct mrz on full=100, the WARP is root cause.
For room 191549, confirm which branch fed recognitionAttempts[0] in getMrzRecognitionAttempts (full attachmentId vs warped candidateId).
Read Raiffeisen’s ocr.engine: VuerCVOCRRecognition (warp-first, matches bug) vs VuerCVMRZDetector/MRZDetectionApi (no warp). Check customization/raiffeisen + instruction.index.
Inspect the warped crop: corners/sharpness — is the bottom MRZ band clipped/skewed/low-res?
Compare 191541 (worse, PASSED) vs 191549 (better, FAILED) → likely corner-detection / aspect-ratio edge case in warp, not raw sharpness.
Diff vuer_cv 4.9.0 mrz_processor.py + warp engine vs prod — does the change touch the WARPED path or only general MRZ robustness?
Check validScoreValidator: is the crop yielding >1 detection (→ forced score 0)?
Confirm the CV version Raiffeisen runs in prod today, and whether 4.9.0 is even deployed there.
Log locations
Quick reference — where to look
# vuer_cvdocker exec -it vuer_cv tail -200 /workspace/vuer_cv/log/app_face.log# grep: document-warp, mrz, valid_score# vuer_osstail /var/log/vuer_oss/error.log# grep: "Error warping image", "Error during OCR",# "Failed to get mrz check result ... (#191549)"
DB:RecognitionAttempt WHERE "selfServiceRoomId"=191549 — compare rawOutputvalid_score of full vs warped attachment.
valid_score only persists when ocr.useCVMRZData=true
valid_score / mrzData are only stored when ocr.useCVMRZData=true. If the flag is off, the scores won’t be visible in the DB.
Quick fixes to try
Run MRZ on the full image, not the crop — switch/override ocr.engine to the no-warp path for this doc class.
Fallback-to-full in the checker — accept the full-photo MRZ attempt when the warped one is below threshold, instead of blindly taking recognitionAttempts[0].
Deploy/verify 4.9.0 on Raiffeisen, then re-run checklist #2(b) on the cropped image.
Loosen the warp crop margin / improve corner detection for TD1 back side in vuer_cv.
Ensure ocr.useCVMRZData=true so scores are persisted/visible.
Escalation path
Zsolt Mészáros (CV/ML) — owns the 4.9.0 claim and mrz_processor.py / warp; give him checklist #2’s result.
Bihari Péter — reporter / SLA owner; keep SLARAFIPI-61 from staying “Solved” until the cropped case is proven.
Bence László — reassigned it to you.
If warp corner-detection is the cause, file a separate vuer_cv bug and ship the vuer_oss mitigation on raiffeisen.
Bottom line
The ticket hinges on checklist #2(b)
Run CV 4.9.0 MRZ on the CROPPED image from the 191549 export. If it still scores 2, the prod path (VuerCVOCRRecognition warp→MRZ) is unfixed, the SLA was closed on a false positive, and the real fix is document-warp crop quality (vuer_cv) and/or switching MRZ to the full image (vuer_oss).
Resolved to Root Cause — the prior triage theory is REFUTED
Ran the actual capture frames from the 191549 (FAILED) and 191541 (PASSED) room exports against the live CV (cv-lederera.facekomdev.net) through the real prod path (/api/v1/image-upload → /api/v2/document-warp → /api/v1/mrz). Both rooms are the same card (doc 010755ZE, SIKE ÁGNES, classId 4 HUN-BO-05001_BACK).
Results (valid_score 0–100; a pass also needs check digits valid)
Room
Input
warp
full→mrz
warp→mrz (prod path)
191541 (PASSED)
3186079 720×384, card fills frame, flat
classId4 0.786
100 ✓
100 ✓
191549 (FAILED)
3186223 1080×720, card small/tilted
classId4 0.815
2 ✗
2 ✗
191549
all 5 back frames 3186203/213/217/221/223
classId4 0.74–0.82
—
2 (all) ✗
191549
export warped crop 3186224 640×400
—
2 ✗
—
Root cause (CORRECTED)
The warp is NOT the culprit. Geometry/classId/score are fine and comparable in both rooms (0.74–0.82). 191549’s own freshly-warped crop still scores 2.
The full image is NOT a safe fallback. 191549’s full image also scores 2. ⇒ the dossier’s proposed vuer_oss mitigations (run-MRZ-on-full / fallback-to-full / candidate-selection) are all refuted — full, crop, and all 5 frames fail.
Real cause = intrinsic capture quality (focus/blur or glare) of the 191549 MRZ region — NOT resolution/framing/distance. Garbled MRZ → valid_score=2 → check digits invalid (valid_number/dob/exp/composite=false) → mrz.valid=false → CHECK_FAILURE → rejection. The same card captured cleanly (191541) reads 100. ⚠️ Resolution/framing/tilt RULED OUT empirically — see UPDATE 2 below.
⚠️ Warp score does NOT distinguish good vs bad captures here (0.74–0.82 in both rooms) — only the MRZ valid_score does. A warp-score-based capture gate would miss this.
SLARAFIPI-61 closure = false positive
The “4.9.0 scored 100” test was run on a good capture (like 191541). The failing capture (191549) still scores 2 on this CV. ⇒ NOT fixed by deploying 4.9.0 — this CV already reads good captures at 100 and the bad capture still fails. (CV version not endpoint-confirmable, but functionally proven to read good HU-eID-back MRZs.)
vuer_oss verdict
No bug in the recognition/checker logic — it correctly rejects an unreadable MRZ. ocr.useCVMRZData=true (prod), minScore default 100. Lowering minScore would NOT help: getMrzCheckResult gates on mrz.valid (check-digit validity), which a garbled MRZ can never satisfy regardless of threshold.
Fix routing (proposed)
Capture-quality gate / guidance (PRIMARY) — flow/vuer_css on customization/raiffeisen: require the back-side capture to fill the frame, be flat & in focus, and pass an MRZ valid_score check before acceptance, with re-prompt. (processMrz already fails the candidate below minScore — verify whether the flow re-prompts or dead-ends into rejection.)
vuer_cv MRZ-OCR robustness — better handling of under-resolved/upscaled MRZ (Zsolt Mészáros). Not branchable locally → file a separate vuer_cv bug.
Reopen SLARAFIPI-61 with this evidence; do not deploy-4.9.0-and-close.
Worktree: vuer_oss/.claude/worktrees/FKITDEV-8788-mrz-td1-crop on feature/FKITDEV-8788-mrz-td1-crop (off origin/customization/raiffeisen, tip 2706094e2b). No code changes made — evidence redirected the fix away from vuer_oss.
Resolution-cliff experiment (refutes the “under-resolved” wording)
Downscaled the known-good 191541 capture and re-ran the prod path (warp → mrz):
Input
card width (px, from warp corners)
valid_score
191549/3186223 (the FAILING capture)
571
2
191541 @ inW=580
285
100
191541 @ inW=460
226
100
191541 @ inW=400
196
53
A sharp capture stays readable down to ~½ the card size; the failing capture has a larger card (571 px) yet scores 2. ⇒ root cause is intrinsic image quality (focus/blur/glare), NOT resolution, framing, distance, or the ~2.4° tilt (warp-corrected). Card bboxes from corners: 191549 = 571×363 (tilt ~2.4°); 191541 = 354×211 (flat).
How the flow handles a bad-MRZ capture TODAY (verified in worktree)
processMrz (customization/listeners/self-service-v2.js:719-724) blocks submit when valid_score < identificationLimits.mrz.minScore (=100), leaving retryEnabled=true (set in customization/flow/myra-self-service-v2-phase-1/...flow.handler.js:131-148); server enforces (SelfServiceV2Service.js:1144-1146). ⇒ the MRZ gate already exists and works — the user is forced to re-capture.
Max 5 attempts (raiffeisen.maxRetryLimits.photo, docker.json:47-48); on exhaustion → this.fail(..., 'no-more-photo-candidate-allowed') (SelfServiceV2Service.js:1104-1111) → room hard-fails.
On failure the server emits only processError:['mrz'] — no helpful “hold steady / avoid glare” message, and no server-side translation. Warp score/sharpness do NOT catch it (warp score 0.74–0.82 in both rooms); only MRZ valid_score does.
No multi-frame aggregation / best-of-N for MRZ (each candidate independent; per-candidate submitEnabled).
Refined fix proposal
vuer_oss (customization/raiffeisen) — processMrz: replace bare processError:['mrz'] with a specific guidance code (optionally retain an early good candidate across retries). Small, in the worktree.
vuer_css (client): capture-screen copy keyed off that code — “Hold the card flat & steady; make sure the bottom machine-readable lines are in sharp focus and glare-free.” (Literal text MUST be client-side; server only emits codes.)
Graceful exhaustion instead of hard room-fail after 5 tries: operator handoff and/or the NFC eMRTD chip read (authoritative, photo-quality-independent; mock is OFF in prod).
vuer_cv (Zsolt Mészáros): MRZ-OCR robustness on degraded-but-legible captures — separate ticket.
Reopen SLARAFIPI-61 (false-positive closure).
Corrections to earlier dossier claims
(a) “FULL image scores 100 / operator full-image manual fallback works” is FALSE for 191549 — the full image also scores 2. (b) The failure is capture quality, not warp-crop, not resolution, not full-vs-crop.