fix(export): vang een mislukte export op zodat het dialoog niet eeuwig hangt (#708) #710

Merged
brenno merged 1 commit from fix/export-hangt-bij-fout into main 2026-07-22 23:12:03 +00:00
Owner

Gevonden tijdens deze sessie uit een "hij hangt gewoon"-melding bij het exporteren. De app stond op 0% processor te slapen — niet te renderen, maar te wachten op iets dat nooit kwam. Dat sloot traagheid uit: het hing echt.

Oorzaak

_ExportDialogState._export() in export_dialog.dart had geen enkele try/catch — in het hele bestand niet één. De functie zet _loading = true, en roept dan SlideRasterizer.rasterize(...) en exportService.export(...) aan. Gooit een van beide — een SlideRasterizerNoFrameException (frame-timeout), een schrijffout, een fout in de PPTX/PDF-assemblage — dan vliegt de uitzondering ongevangen naar buiten en wordt de regel die _loading = false zet nooit bereikt. Het dialoog blijft op zijn "…samenstellen…"-fasetekst staan, voor altijd, zonder melding.

De bestaande foutafhandeling dekte alleen een teruggegeven ExportResult(success: false). Een gegooide uitzondering ging daar volledig langs.

Waarom dit meer dan cosmetisch is

Een export die vastloopt op een missend bestand of een render-hapering laat de gebruiker met een dood venster achter — sluiten is de enige uitweg, en er is geen spoor van wat er misging. Voor een product dat exporteren als kernfunctie heeft, is "faalt stil en voorgoed" de slechtste uitkomst.

De reparatie

De render- en exportstap zitten nu in een try/catch. Bij een fout: loggen mét stacktrace, _loading uit, en de melding tonen — met de technische reden op een eigen regel, want "de export is mislukt" alleen laat je met niets achter.

_export is opgesplitst in een dunne omhulling (de try/catch) en _runExport (de body, ongewijzigd erin). De omhulling zit op het niveau dat óók de rasterizer-timeout vangt — dat was de oorspronkelijke verdenking toen dit opdook.

Getoetst

  • Deterministische reproductie. Een ExportService die gooit, geëxporteerd als HTML (dat slaat de rasterizer over, dus de fout landt recht in het dialoog). Zonder de fix loopt het venster vast op de laadtekst "…samenstellen…"; ermee verschijnt de foutmelding. De test is dus rood vóór en groen ná — dat is de mutatie-evidentie ineen.
  • Eén nieuwe melding × 31 talen (gedelegeerd, make l10n-check groen).
  • make check groen, make check-secrets en make sast schoon.

Herkomst van de melding

Ik had de traagheid eerst ten onrechte op inherente rendering en op #613 (de UI-thread-scans) gegooid. Dat klopte niet: de diagnose via de procestoestand (0% CPU, sleeping) en de code wees eenduidig hierheen. #613 is een echte, aparte traagheid vóór het renderen; dit is een deadlock ná een fout. Twee verschillende dingen.

Closes #708

Gevonden tijdens deze sessie uit een "hij hangt gewoon"-melding bij het exporteren. De app stond op **0% processor te slapen** — niet te renderen, maar te wachten op iets dat nooit kwam. Dat sloot traagheid uit: het hing echt. ## Oorzaak `_ExportDialogState._export()` in `export_dialog.dart` had **geen enkele `try/catch`** — in het hele bestand niet één. De functie zet `_loading = true`, en roept dan `SlideRasterizer.rasterize(...)` en `exportService.export(...)` aan. Gooit een van beide — een `SlideRasterizerNoFrameException` (frame-timeout), een schrijffout, een fout in de PPTX/PDF-assemblage — dan vliegt de uitzondering ongevangen naar buiten en wordt de regel die `_loading = false` zet nooit bereikt. Het dialoog blijft op zijn "…samenstellen…"-fasetekst staan, voor altijd, zonder melding. De bestaande foutafhandeling dekte alleen een *teruggegeven* `ExportResult(success: false)`. Een *gegooide* uitzondering ging daar volledig langs. ## Waarom dit meer dan cosmetisch is Een export die vastloopt op een missend bestand of een render-hapering laat de gebruiker met een dood venster achter — sluiten is de enige uitweg, en er is geen spoor van wat er misging. Voor een product dat exporteren als kernfunctie heeft, is "faalt stil en voorgoed" de slechtste uitkomst. ## De reparatie De render- en exportstap zitten nu in een `try/catch`. Bij een fout: loggen mét stacktrace, `_loading` uit, en de melding tonen — met de technische reden op een eigen regel, want *"de export is mislukt"* alleen laat je met niets achter. `_export` is opgesplitst in een dunne omhulling (de `try/catch`) en `_runExport` (de body, ongewijzigd erin). De omhulling zit op het niveau dat óók de rasterizer-timeout vangt — dat was de oorspronkelijke verdenking toen dit opdook. ## Getoetst - **Deterministische reproductie.** Een `ExportService` die gooit, geëxporteerd als HTML (dat slaat de rasterizer over, dus de fout landt recht in het dialoog). Zonder de fix loopt het venster vast op de laadtekst "…samenstellen…"; ermee verschijnt de foutmelding. De test is dus rood vóór en groen ná — dat is de mutatie-evidentie ineen. - Eén nieuwe melding × 31 talen (gedelegeerd, `make l10n-check` groen). - `make check` groen, `make check-secrets` en `make sast` schoon. ## Herkomst van de melding Ik had de traagheid eerst ten onrechte op inherente rendering en op #613 (de UI-thread-scans) gegooid. Dat klopte niet: de diagnose via de procestoestand (0% CPU, `sleeping`) en de code wees eenduidig hierheen. #613 is een echte, aparte traagheid vóór het renderen; dit is een deadlock ná een fout. Twee verschillende dingen. Closes #708
Gevonden uit een "hij hangt gewoon"-melding tijdens een export. De app stond op
0% processor te slapen — niet te renderen, maar te wachten op iets dat nooit
kwam.

`_ExportDialogState._export()` had geen enkele `try/catch`. De functie zet
`_loading = true` en roept dan `SlideRasterizer.rasterize(...)` en
`exportService.export(...)` aan. Gooit een van beide — een frame-timeout in de
rasterizer, een schrijffout, een fout in de PPTX/PDF-assemblage — dan vloog de
uitzondering ongevangen naar buiten en werd de regel die `_loading` uitzet nooit
bereikt. Het dialoog bleef op zijn "…samenstellen…"-fasetekst staan, voor
altijd, zonder melding.

De bestaande foutafhandeling dekte alleen een *teruggegeven*
`ExportResult(success: false)`. Een *gegooide* uitzondering ging daar volledig
langs.

De render- en exportstap zitten nu in een `try/catch`. Bij een fout: loggen met
stacktrace, `_loading` uit, en de melding tonen — met de technische reden op een
eigen regel, want "de export is mislukt" alleen laat de gebruiker met niets
achter. De omhulling zit op het niveau dat óók de rasterizer-timeout vangt.

`_export` is opgesplitst in een dunne omhulling (de `try/catch`) en `_runExport`
(de body die er ongewijzigd in zit). Regressietest via het HTML-pad: dat slaat
de rasterizer over, dus een gooiende service landt recht in het dialoog — de
test ziet het venster vastlopen op de laadtekst zonder de fix, en de melding
verschijnen ermee.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit 6aefd9bc84 into main 2026-07-22 23:12:03 +00:00
Sign in to join this conversation.
No description provided.