[Bug]: Dispatcher swallows controller-invocation TypeError as an empty, unlogged 400 - admin cannot diagnose silently-failed saves (repro: changing the SMTP port)
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 36.9k
- Forks
- 5.2k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 713
Description
⚠️ This issue respects the following points: ⚠️
- This is a bug, not a question or a configuration/webserver/proxy issue.
- This issue is not already reported on Github OR Nextcloud Community Forum (I've searched it).
- Nextcloud Server is up to date. See Maintenance and Release Schedule for supported versions.
- I agree to follow Nextcloud's Code of Conduct.
Bug description
When a controller method is invoked with an argument whose type does not match the method signature, lib/private/AppFramework/Http/Dispatcher.php catches the resulting TypeError at the invocation boundary and returns an empty HTTP 400 - without logging anything. From an administrator's point of view the request simply fails with a generic client-side AxiosError: Request failed with status code 400, no server-side log entry exists at any log level, and there is no way to tell a genuinely-handled bad request apart from an internal type mismatch.
This turns any form/controller type-coercion mismatch into a silent, undiagnosable failure in production. The concrete, fully reproducible instance below is saving the mail settings after changing the SMTP port, but the underlying diagnostic gap is general and is the primary point of this report.
This is a recurring class of bug in Nextcloud (see "Prior art" below), which is why the systemic diagnostic issue matters more than any single field fix.
Steps to reproduce
- Go to Settings > Administration > Basic settings > Email server.
- Change the "Port" field to any value (e.g. 587 → 465).
- Trigger save (click outside the field).
- Observe: a generic "saving failed" notification.
- Verify persistence:
occ config:system:get mail_smtpportis unchanged — the save genuinely did not persist (not merely a UI/notification glitch). - Check
nextcloud.log: no corresponding error/exception entry, at any log level. - Check the webserver access log: the POST to
/settings/admin/mailsettingsreturns400withContent-Length: 0.
Expected behavior
Primary (the point of this report):
- When the Dispatcher catches a
TypeErrorat the controller-invocation boundary, it should log it (even at debug/info level) with enough context (controller, method, offending parameter/type) so administrators can diagnose an otherwise-silent 400.
Secondary (fix the concrete case):
- The port field should serialize as a string matching the controller signature (cast back to string before submit, or use a text input with numeric validation instead of
type="number"), and/or MailSettingsController::setMailSettings()should acceptint|string $mail_smtpportand cast internally, since numeric form fields are prone to exactly this coercion mismatch.
Nextcloud Server version
34
Operating system
Debian/Ubuntu
PHP engine version
PHP 8.4
Web server
Nginx
Database engine version
MariaDB
Is this bug present after an update or on a fresh install?
Fresh Nextcloud Server install
Are you using the Nextcloud Server Encryption module?
None
What user-backends are you using?
- Default user-backend (database)
- LDAP/ Active Directory
- SSO - SAML
- Other
Configuration report
List of activated Apps
Nextcloud Signing status
Nextcloud Logs
Additional info
Root cause (traced through the code)
- The port field is bound in the settings frontend as a numeric input:
Because it is<NcTextField v-model="mailConfig.mail_smtpport" type="number" max="65535" min="1" .../>type="number", once edited the bound value becomes a JavaScript number. - The save handler POSTs the whole
mailConfigobject as JSON (axios.post(generateUrl('/settings/admin/mailsettings'), config)), somail_smtpportis serialized as a JSON number (465) rather than a string ("465"). OCA\Settings\Controller\MailSettingsController::setMailSettings()declares the parameter as non-nullablestring $mail_smtpport.lib/private/AppFramework/Http/Dispatcher.phphasdeclare(strict_types=1). PHP determines strict-typing from the calling file, so the controller invocation is strictly typed: passing a JSON number where astringis required throws aTypeErrorat the invocation line.- The Dispatcher catches exactly this and returns an empty 400, intentionally not logging it (to distinguish it from TypeErrors thrown inside controller logic):
} catch (\TypeError $e) { // Only intercept TypeErrors occurring on the first line, meaning that the // invocation of the controller method failed. // Any other TypeError happens inside the controller method logic and should be logged as normal. ... return new Response(Http::STATUS_BAD_REQUEST); } - Net effect:
setSystemValues()insidesetMailSettings()never runs, the new port is silently discarded, and there is zero diagnostic trail — no server log entry, only a generic AxiosError in the browser.
Why the diagnostic gap is the real issue
The specific port mismatch could be fixed in one line on either side (see below). But the reason this took hours to diagnose — and the reason similar reports keep recurring under different symptoms — is that the Dispatcher gives operators nothing to go on. An empty, unlogged 400 at the controller-invocation boundary is indistinguishable, from the outside, from any other bad request, and it is invisible even at the lowest log level (loglevel 0).
Any current or future controller whose signature doesn't match what a form actually serializes will fail this same silent way. Fixing individual fields does not close the class of bug; making the boundary observable does.
Workaround (for other admins hitting this)
Set the value directly via occ, bypassing the web form and its JSON binding entirely:
occ config:system:set mail_smtpport --value=<port>
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with lib/private/AppFramework/Http/Dispatcher.php and trace the TypeError handling at controller invocation, then inspect MailSettingsController::setMailSettings() and the SMTP port field binding. Reproduce the save from Administration > Basic settings > Email server and verify that the invocation failure produces a contextual server log while the request behavior remains distinguishable from a handled bad request.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, php
- Domain
- backend, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100