BookStackApp/BookStack · error · HttpFetchException

errors.cannot_get_image_from_url

Error message

errors.cannot_get_image_from_url

What it means

BookStack's UserAvatars::getAvatarImageData() fetches an external image via an HTTP client and throws HttpFetchException carrying the translated message errors.cannot_get_image_from_url when the response status is not 200. It is a guard against writing non-image/garbage responses (redirects, 403/404 pages, rate-limit blocks) into user avatar storage. The URL is included in the message so the failing remote source can be identified.

Source

Thrown at app/Uploads/UserAvatars.php:158

    protected function getAvatarImageData(string $url): string
    {
        try {
            $client = $this->http->buildClient(5);
            $responseCount = 0;

            do {
                $response = $client->sendRequest(new Request('GET', $url));
                $responseCount++;
                $isRedirect = ($response->getStatusCode() === 301 || $response->getStatusCode() === 302);
                $url = $response->getHeader('Location')[0] ?? '';
            } while ($responseCount < 3 && $isRedirect && str_starts_with($url, 'http'));

            if ($responseCount === 3) {
                throw new HttpFetchException("Failed to fetch image, max redirect limit of 3 tries reached. Last fetched URL: {$url}");
            }

            if ($response->getStatusCode() !== 200) {
                throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url]));
            }

            return (string) $response->getBody();
        } catch (ClientExceptionInterface $exception) {
            throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url]), $exception->getCode(), $exception);
        }
    }

    /**
     * Check if fetching external avatars is enabled.
     */
    public function avatarFetchEnabled(): bool
    {
        $fetchUrl = $this->getAvatarUrl();

        return str_starts_with($fetchUrl, 'http');
    }

View on GitHub (pinned to 18f8469a1c)

Solutions

  1. Open the URL in the message from a server-side context (curl) and confirm it returns 200 with image content-type; fix or replace the URL
  2. Check for hotlink protection / User-Agent blocking on the remote host and use a CDN or locally-uploaded avatar instead
  3. Verify outbound HTTP (proxy/firewall) allows the request and no middleware rewrites the URL to an error page
  4. Catch HttpFetchException in calling code and surface a friendly message pointing at the avatar URL

Example fix

// before
$userAvatars->assignToUserFromUrl($user, 'https://example.com/avatar.png');
// after
try {
    $userAvatars->assignToUserFromUrl($user, $verifiedUrl);
} catch (HttpFetchException $e) {
    // pre-validate or fall back to default avatar
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate URL is fetchable and returns an image before assigning
$resp = \Illuminate\Support\Facades\Http::timeout(5)->get($url);
if ($resp->failed() || !str_starts_with($resp->header('Content-Type') ?? '', 'image/')) {
    throw new \InvalidArgumentException("Avatar URL not fetchable: {$url}");
}

Type guard

function isHttpOkResponse(?\Psr\Http\Message\ResponseInterface $r): bool {
    return $r !== null && $r->getStatusCode() === 200;
}

Try / catch

try {
    $avatars->assignToUserFromUrl($user, $url);
} catch (\BookStack\Exceptions\HttpFetchException $e) {
    Log::warning('Avatar fetch failed', ['url' => $url, 'msg' => $e->getMessage()]);
    // fall back to default avatar
}

Prevention

When it happens

Trigger: Calling UserAvatars::assignToUserFromUrl or saveAvatarImage with a URL whose server replies 404/403/401/429/500 etc. (anything !== 200). Notably NOT thrown for >3 redirects (that throws a distinct max-redirect message).

Common situations: Hotlinking-protection returning 403 for non-browser user agents; typo'd or deleted image URLs; expired signed URLs (S3 presigned links); URLs requiring auth cookies; remote hosts rate-limiting BookStack's server-side fetch.

Related errors


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/5b0f749373b54cdd. Report an issue: GitHub.