Steps to reproduce

  1. Enable the Link checker module and let it extract links from content as usual.
  2. 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.
  3. Let Link checker queue that link for checking (via cron, or the linkchecker_check queue worker directly).
  4. 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.

Command icon 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

bond708 created an issue. See original summary.

joelpittet’s picture

Status: Active » Needs work

The merge request needs rerolling

joelpittet’s picture

Issue tags: -Guzzle, -cron +Needs reroll

liam morland made their first commit to this issue’s fork.

liam morland’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll

joelpittet changed the visibility of the branch 2.1.x to hidden.

joelpittet’s picture

Thanks 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.

joelpittet’s picture

Status: Needs review » Fixed

Thank you @bond708 and @liam morland for getting this in. I added the regression test to ensure it stays as such.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • joelpittet committed cac94139 on 2.1.x authored by bond708
    fix: #3622994 Uncaught MalformedUriException from Guzzle crashes cron...