enhancement
major
minor
major
minor
major
#29560
React view <upload-command>: Fehler beim Upload (Ausnahme in der Aktionskette, überschrittenes Größenlimit) erreichen den Benutzer nicht; Upload-Größenlimit konfigurierbar
Affects 8.0.0-SNAPSHOT (2026-09-11), tl-layout-view / tl-layout-react.
Ausnahme in der Aktionskette wird verschluckt
ViewUploadCommandModel runs the configured action chain once per uploaded file. When the chain throws — observed with the TopLogicException "The user is not allowed to create elements of type Ausgabe" out of new(`expenses:Expense`) for an account without a CREATE grant — the model writes
ERROR ViewUploadCommandModel - Upload failed for file 'receipt-hotel.pdf'. com.top_logic.util.error.TopLogicException: The user is not allowed to create elements of type Ausgabe. at com.top_logic.model.search.expr.CreateObject.eval(CreateObject.java:52)
to the log and continues with the next file. Nothing reaches the browser: no snackbar, no error dialog, the table stays as it was. The user has no way to tell a refused upload from a slow one.
A TopLogicException is by contract a message for the user. Expected: the exception's message is forwarded to the window's ErrorSink (the same path info() entries take through ReactServlet.forwardPendingUpdates), one notice per failed file, and the log entry stays at INFO or WARN for a TopLogicException — it is a refused operation, not a defect.
Application-side workaround: none for a refusal thrown by the engine itself; a failure the application detects itself can say so with the TL-Script info() function instead of throwing.
Größenlimit: HTTP 500 ohne Hinweis, Limit fest verdrahtet (vormals #29566)
Affects 8.0.0-SNAPSHOT (2026-09-12), tl-layout-react (ReactServlet, META-INF/web-fragment.xml).
Upload a 57 MB PDF through a React view <upload-command>. Jetty rejects the multipart body:
IllegalStateException: max length exceeded: 52428800 BadMessageException: 400: bad multipart ERROR … ReactServlet - Error handling React request: /upload
The servlet answers HTTP 500 {"success":false,"error":"Internal error."}; the client logs it to the console and shows nothing — no notice, no dialog, the upload button simply does nothing from the user's point of view.
Two defects:
- The refusal does not reach the user. An upload the container refuses for its size must produce a notice naming the limit ("Files up to 50 MB"), the same way a refused command does.
- The limit cannot be configured. max-file-size / max-request-size = 52428800 are literals in tl-layout-react's META-INF/web-fragment.xml (metadata-complete="true"), so an application cannot raise or lower them and cannot even read them to tell the user. Expected: a configuration property (application-level) whose value the client also gets, so the file picker can refuse early and the notice can name the number.
Found while measuring receipt size limits for the Expenses application (which needs a stated maximum for photographed receipts).
Lösung
Gemeinsame Fehlerausgabe. Die Umwandlung einer Ausnahme in ein Fehlerergebnis (INFO-Log für Benutzerfehler, ERROR für interne Fehler, Übernahme des I18N-Schlüssels) und die Darstellung eines fehlgeschlagenen HandlerResult im ErrorSink des Fensters (Überschrift plus Detailliste) liegen im öffentlichen Helfer com.top_logic.layout.react.control.CommandErrors in tl-layout-react, den Kommando-Dispatch (ReactCommandInvoker), ReactServlet und Upload-Kommando gemeinsam nutzen.
Fehler in der Aktionskette. ViewUploadCommandModel meldet jede fehlgeschlagene Datei über diesen Helfer im ErrorSink: Überschrift „Upload von „<Datei>“ fehlgeschlagen.“, darunter die Fehlermeldung der Kette; die übrigen Dateien werden weiter verarbeitet. Eine TopLogicException wird mit INFO protokolliert, interne Fehler weiterhin mit ERROR. Der Upload-Endpunkt des ReactServlet zeigt ein Fehlerergebnis oder eine Ausnahme des Upload-Handlers ebenfalls im ErrorSink an statt mit HTTP 500 zu antworten.
Snackbar-Warteschlange. Damit bei mehreren Dateien jede Meldung sichtbar wird, zeigt ReactSnackbarControl eine Meldung, die während einer sichtbaren eintrifft, nicht mehr sofort an deren Stelle, sondern reiht sie ein und zeigt sie nach dem Ausblenden der vorherigen (Reihenfolge des Eintreffens, jede mit eigener Generation und eigenem Timer).
Konfigurierbares Größenlimit. Anwendungskonfiguration com.top_logic.layout.react.control.upload.UploadConfig in tl-layout-react (registriert in den <configs> des Moduls) mit der Eigenschaft max-upload-size (MemorySizeFormat, Standard 50 MB, 0 = unbegrenzt). Sie begrenzt jede einzelne Datei und die gesamte Anfrage. Beispiel:
<configs> <config config:interface="com.top_logic.layout.react.control.upload.UploadConfig" max-upload-size="10m" /> </configs>
Durchsetzung. Die web-fragment.xml behält nur die Spool-Schwelle. Das ReactServlet setzt das konfigurierte Limit selbst und containerunabhängig durch: Content-Length vor dem Parsen, Part-Größen nach dem Parsen; zusätzlich wird Jetty das Limit über das Request-Attribut org.eclipse.jetty.multipartConfig mitgegeben, so dass der Container das Einlesen am Limit abbricht. Eine Ablehnung zeigt „Es können Dateien bis 10 MB hochgeladen werden.“ im ErrorSink, protokolliert mit INFO und antwortet mit HTTP 413.
Frühe Ablehnung im Client. Jedes Upload-Control legt das Limit unter dem Zustandsschlüssel maxUploadSize ab (UploadSupport). useTLUpload() in der React-Bridge prüft die gewählten Dateien (einzeln und in Summe) vor dem Senden und schickt bei Überschreitung statt des Uploads das globale Kommando uploadRejected, auf das das Servlet mit demselben Hinweis antwortet. Das deckt alle Upload-Controls ab (Upload-Button, Datei-Upload, Binärfeld, Foto, Audio).
Demo. Die Fehlerbehandlungs-Demo der React-Demo-Anwendung (demo/error-handling-demo.view.xml) enthält ein <upload-command> „Upload (Kette schlägt fehl)“, dessen Kette einen Fehler wirft; die Demo-Konfiguration setzt max-upload-size="10m".