No Code Attached Yet bug
avatar CyrusXxXxX
CyrusXxXxX
29 Aug 2026

What happened?

Environment:
Joomla 6.1.3, PHP 8.4.17, Apache 2.4 (mod_fcgid), MySQL 8.4.7. Reproduced on a clean request, no authentication, no third-party code in the path.

Summary

CMSApplication::sanityCheckSystemVariables() correctly rejects a request when a reserved system variable arrives as an array. That part works as designed and has since 3.9. The problem is what happens next: the exception it throws cannot be rendered, because rendering an error page requires an admin-only class whose namespace has not been registered yet. The controlled RuntimeException is therefore replaced by an uncaught ClassNotFoundError and the visitor gets a bare HTTP 500.

Steps to reproduce

On any Joomla 6.1.3 site, as an anonymous visitor:

curl -i "https://example.org/?task[]=x"

Result: HTTP/1.1 500. The same happens for each of the eight reserved names, on the site, on /administrator/ and on /api/, over GET and over POST:

?option[]=x   500      ?Itemid[]=x         500
?view[]=x     500      ?template[]=x       500
?format[]=x   500      ?templateStyle[]=x  500
?lang[]=x     500      ?task[]=x           500

Control: ?limit[]=5 returns 200, and a clean request immediately after any of the above returns 200 (the failure is per-request, it does not poison later requests).

With debug enabled the response body contains the full exception chain and absolute file paths.

Why it happens

  1. libraries/src/Application/CMSApplication.php:267-288 - sanityCheckSystemVariables() collects any of option, view, format, lang, Itemid, template, templateStyle, task that arrive as arrays and throws new \RuntimeException('Invalid input, aborting application.'). This is intentional (@since 3.9, from PR #28751).

  2. It is called from execute() at :300 - the first statement in the try block, before createExtensionNamespaceMap() at :302 and before any plugin is imported at :305/:308.

  3. The catch block at :338-351 hands the exception to ExceptionHandler::handleException() -> render() (libraries/src/Exception/ExceptionHandler.php:88-200), which calls HtmlRenderer::render() at :142.

  4. libraries/src/Error/Renderer/HtmlRenderer.php:50 needs the active template, so it calls $app->getTemplate(true) -> SiteApplication::initialiseTemplate() -> getTemplates() -> bootComponent('templates') (ExtensionManagerTrait.php:42-52,113-187) -> administrator/components/com_templates/services/provider.php:46, new TemplatesComponent(...).

  5. That class cannot be autoloaded. The PSR-4 entry mapping Joomla\Component\Templates\Administrator\ to administrator/components/com_templates/src is created by createExtensionNamespaceMap() - step 2, which never ran. The result is a Symfony\Component\ErrorHandler\Error\ClassNotFoundError.

  6. That second throwable escapes the try/catch inside ExceptionHandler::render() at :167; the code at :176-196 merges the two and rethrows. Nothing inside execute() can catch it now, so it travels through includes/app.php:58 and index.php:51 to the global Symfony handler registered in libraries/bootstrap.php:49, which emits HTTP 500.

com_templates has nothing to do with the original request - it is simply the first administrator-only class any themed error page must load, so it is where the missing namespace map surfaces.

Impact

Any anonymous visitor can make every page of a Joomla site return 500 with a single query parameter. There is no persistent damage and no privilege gain: the request is refused as intended, and the next request succeeds. The concrete harm is that a deliberate, well-defined refusal is presented as a server fault - it fills error logs, it can trip uptime monitoring, and on a site left in debug mode it discloses full filesystem paths.

Suggested direction

The general problem is that HtmlRenderer assumes a bootable extension namespace map, while sanityCheckSystemVariables() (and potentially anything else failing that early) runs before one exists. Two options that seem open to you:

  • Move createExtensionNamespaceMap() above sanityCheckSystemVariables() in execute(), so the error renderer always has what it needs.
  • Or make the error path degrade safely: have HtmlRenderer (or ExceptionHandler::render()) fall back to the plain-text/minimal renderer when the template cannot be resolved, instead of letting a second throwable replace the first.

The second seems more broadly useful, since the same class of failure has been reported for other early errors - see issue #39653, where a database failure produces the same "error renderer cannot render" outcome through a different trigger.

Related

  • PR #28751 - origin of the guard itself.
  • Issue #47318 / PR #47319 - a different array-input bug (id[] in PreprocessRules.php:106). Worth noting we also observe ?id[]=5 returning 500 on some routes on 6.1.3 while /some-page?id[]=5 returns 200, which looks like that separate issue rather than this one.
  • Issue #34748, issue #39653 - same architectural fragility (error renderer needs a template), different triggers.

Thank you!

Version

6.1

Expected result

No response

Actual result

No response

System Information

Joomla 6.1.3, PHP 8.4.17, Apache 2.4 (mod_fcgid), MySQL 8.4.7. Reproduced on a clean request, no authentication, no third-party code in the path.

Additional Comments

No response

avatar CyrusXxXxX CyrusXxXxX - open - 29 Aug 2026
avatar joomla-cms-bot joomla-cms-bot - change - 29 Aug 2026
Labels Added: No Code Attached Yet bug
avatar joomla-cms-bot joomla-cms-bot - labeled - 29 Aug 2026
avatar Fedik
Fedik - comment - 30 Aug 2026

Thanks for reaport.
As you wrote:

There is no persistent damage and no privilege gain: the request is refused as intended,

About:

The concrete harm is that a deliberate, well-defined refusal is presented as a server fault - it fills error logs, it can trip uptime monitoring, and on a site left in debug mode it discloses full filesystem paths.

This is configuration mistake, which we cannot cover any possible misconfiguration.

I suggest to close the issue.

avatar Fedik
Fedik - comment - 30 Aug 2026

Thanks for reaport.
As you wrote:

There is no persistent damage and no privilege gain: the request is refused as intended,

And about:

The concrete harm is that a deliberate, well-defined refusal is presented as a server fault - it fills error logs, it can trip uptime monitoring, and on a site left in debug mode it discloses full filesystem paths.

This is configuration mistake, which we cannot cover any possible misconfiguration.

I suggest to close the issue.

avatar joomdonation
joomdonation - comment - 30 Aug 2026

@Fedik I think we should not throw fatal error because of wrong inputs like that. The suggested seems also OK, too

Move createExtensionNamespaceMap() above sanityCheckSystemVariables() in execute()

Add a Comment

Login with GitHub to post a comment