Steps to reproduce
- Enable the Link checker module and let it extract links from content as usual.
- Have a link somewhere in content whose URL is malformed in a way Guzzle's URI parser rejects — e.g. it contains a space in what gets parsed as the host, such as
https://example - domain.com. This can happen when an editor pastes plain text into a link field instead of a real URL. - Let Link checker queue that link for checking (via cron, or the
linkchecker_checkqueue worker directly). - Run cron.
Cron fails with an uncaught exception:
GuzzleHttp\Psr7\Exception\MalformedUriException: Invalid host: "example - domain.com" in GuzzleHttp\Psr7\Uri->__construct() (line 76 of vendor/guzzlehttp/psr7/src/Uri.php).Cron stops entirely (not just link checking), and every subsequent cron run fails the same way until the offending row is manually removed from the linkchecker_link table.
Proposed resolution
LinkCheckerService::check() calls Client::requestAsync(), which synchronously builds a GuzzleHttp\Psr7\Uri for the given URL before returning a promise. If Guzzle's URI parser rejects the URL (e.g. MalformedUriException, thrown since guzzlehttp/psr7 2.10.2's stricter host validation), the exception is thrown immediately — before any promise exists — so it is never caught by the existing function (RequestException $e) rejection handler passed to ->then(). The exception propagates out of LinkCheck::processItem() and crashes cron.
Attached patch wraps the requestAsync() call in a try/catch (\Throwable $e) and routes any exception through the existing exceptionHandling() method (widening its parameter type from RequestException to \Throwable), so a malformed URL is recorded as a broken link (status 502, logged) instead of crashing cron. This is a superset fix of #3607990 "Trim links" — that issue only prevents leading/trailing-whitespace URLs from becoming malformed in the first place, but doesn't add any safety net for other cases Guzzle can reject (invalid characters, internal whitespace, etc), so cron can still crash on any URL not covered by trimming.
Remaining tasks
- Review patch
- Add test coverage for a malformed URL not crashing the queue worker
- Commit
User interface changes
None.
API changes
LinkCheckerService::exceptionHandling() parameter type widened from \GuzzleHttp\Exception\RequestException to \Throwable.
Data model changes
None.
Issue fork linkchecker-3622994
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
joelpittetThe merge request needs rerolling
Comment #4
joelpittetComment #6
liam morlandComment #8
joelpittetThanks for the reroll, I’ll have a look at the code.
If someone can do a small regression test that would make this a slam dunk.
Comment #9
joelpittetThank you @bond708 and @liam morland for getting this in. I added the regression test to ensure it stays as such.