Yet another wkhtmltopdf thread

DNS ----> Try running “curl -vI http://web.erp.domain”. Once the domain responds, wkhtmltopdf will work without any issues.

Of course, you need to make sure it’s accessible.

In my case, using a proxy, I edited the /etc/hosts file and, instead of using the local server’s IP, I used the proxy’s IP. That’s it—no other changes needed.

Hi folks,

I’m experiencing a similar issue, if not the same one, as discussed below: Help Needed: Error with PDF Generation when Sending Email.

I’m unable to send emails using my Gmail account, and I also can’t print invoices. I have already set the host_name to my site name, which is proxied through Nginx Proxy Manager, but the issue persists.

Has anyone found a solution or have any suggestions on what else I might try? Thanks in advance for your help!

Confirm if the site location is accessible from within workers and gunicorn containers. If the process cannot connect to host_name it’ll not be able to generate pdf.

Access from browser is different, that you already have.

Use extra_hosts or figure out way to resolve images from containers.

Thank you for your prompt response! I would appreciate your assistance, and I’ll gladly treat you to lunch for your help. :sweat_smile:

I’m currently running Docker with WSL, Portainer, Nginx Proxy Manager, and a Cloudflare tunnel in a container. The frontend is proxied through Nginx and is accessible via the internet through the Cloudflare tunnel. All containers have external site access, so I don’t need to add extra_hosts directives (which I believe have been deprecated). They are utilizing the Docker bridge network. For example, I’m able to successfully run:

curl https://test.menteware.com

Here’s the stack configuration I have set up in Portainer:

version: "3"

services:
  backend:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

  configurator:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: none
    entrypoint:
      - bash
      - -c
    # add redis_socketio for backward compatibility
    command:
      - >
        ls -1 apps > sites/apps.txt;
        bench set-config -g db_host $$DB_HOST;
        bench set-config -gp db_port $$DB_PORT;
        bench set-config -g redis_cache "redis://$$REDIS_CACHE";
        bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
        bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
        bench set-config -gp socketio_port $$SOCKETIO_PORT;
    environment:
      DB_HOST: db
      DB_PORT: "3306"
      REDIS_CACHE: redis-cache:6379
      REDIS_QUEUE: redis-queue:6379
      SOCKETIO_PORT: "9000"
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

  create-site:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: none
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs
    entrypoint:
      - bash
      - -c
    command:
      - >
        wait-for-it -t 120 db:3306;
        wait-for-it -t 120 redis-cache:6379;
        wait-for-it -t 120 redis-queue:6379;
        export start=`date +%s`;
        until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
          [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
          [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
        do
          echo "Waiting for sites/common_site_config.json to be created";
          sleep 5;
          if (( `date +%s`-start > 120 )); then
            echo "could not find sites/common_site_config.json with required keys";
            exit 1
          fi
        done;
        echo "sites/common_site_config.json found";
        bench new-site --no-mariadb-socket --admin-password="${ADMIN_PWD}" --db-root-password="${DB_ROOT_PWD}" --install-app erpnext --set-default "${SITE_NAME}";
        bench set-config -g host_name "https://${SITE_NAME}";
        bench --site "${SITE_NAME}" set-config host_name "https://${SITE_NAME}";
        bench --site "${SITE_NAME}" install-app payments;
        bench --site "${SITE_NAME}" install-app hrms;
        bench --site "${SITE_NAME}" install-app ecommerce_integrations;
        bench --site "${SITE_NAME}" install-app webshop;
        bench --site "${SITE_NAME}" install-app print_designer;
        # bench --site "${SITE_NAME}" migrate
        # bench clear-cache
        # bench build

        
      

  db:
    image: mariadb:10.6
    healthcheck:
      test: mysqladmin ping -h localhost --password="${DB_ROOT_PWD}"
      interval: 1s
      retries: 15
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake
      - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
    environment:
      MYSQL_ROOT_PASSWORD: "${DB_ROOT_PWD}"
    volumes:
      - db-data:/var/lib/mysql

  frontend:
    image: edardev/erpnext:v15.38.1-print
    depends_on:
      - websocket
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - nginx-entrypoint.sh
    environment:
      BACKEND: backend:8000
      FRAPPE_SITE_NAME_HEADER: "${SITE_NAME}"
      SOCKETIO: websocket:9000
      UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
      UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
      UPSTREAM_REAL_IP_RECURSIVE: "off"
      PROXY_READ_TIMEOUT: 120
      CLIENT_MAX_BODY_SIZE: 50m
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs
    ports:
      - "${FRONTEND_PORT:-9999}:8080"

  queue-long:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - bench
      - worker
      - --queue
      - long,default,short
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

  queue-short:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - bench
      - worker
      - --queue
      - short,default
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

  redis-queue:
    image: redis:6.2-alpine
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - redis-queue-data:/data

  redis-cache:
    image: redis:6.2-alpine
    deploy:
      restart_policy:
        condition: on-failure
    volumes:
      - redis-cache-data:/data

  scheduler:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - bench
      - schedule
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

  websocket:
    image: edardev/erpnext:v15.38.1-print
    deploy:
      restart_policy:
        condition: on-failure
    command:
      - node
      - /home/frappe/frappe-bench/apps/frappe/socketio.js
    volumes:
      - sites:/home/frappe/frappe-bench/sites
      - logs:/home/frappe/frappe-bench/logs

volumes:
  db-data:
  redis-queue-data:
  redis-cache-data:
  sites:
  logs:


And this is error from Frappe when sending the queue email with the document attached:

Traceback (most recent call last):
  File "apps/frappe/frappe/utils/pdf.py", line 90, in get_pdf
    filedata = pdfkit.from_string(html, options=options or {}, verbose=True)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pdfkit/api.py", line 75, in from_string
    return r.to_pdf(output_path)
           ^^^^^^^^^^^^^^^^^^^^^
  File "env/lib/python3.11/site-packages/pdfkit/pdfkit.py", line 201, in to_pdf
    self.handle_error(exit_code, stderr)
  File "env/lib/python3.11/site-packages/pdfkit/pdfkit.py", line 155, in handle_error
    raise IOError('wkhtmltopdf reported an error:\n' + stderr)
OSError: wkhtmltopdf reported an error:
Exit with code 1 due to network error: ContentOperationNotPermittedError


During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "apps/frappe/frappe/email/doctype/email_queue/email_queue.py", line 167, in send
    message = ctx.build_message(recipient.recipient)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/email/doctype/email_queue/email_queue.py", line 315, in build_message
    message = self.include_attachments(message)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/email/doctype/email_queue/email_queue.py", line 392, in include_attachments
    print_format_file = frappe.attach_print(**attachment)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/__init__.py", line 2232, in attach_print
    else get_print(doctype, name, **kwargs)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/__init__.py", line 2191, in get_print
    return get_pdf(html, options=pdf_options, output=output) if as_pdf else html
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "apps/frappe/frappe/utils/pdf.py", line 98, in get_pdf
    frappe.throw(_("PDF generation failed because of broken image links"))
  File "apps/frappe/frappe/__init__.py", line 652, in throw
    msgprint(
  File "apps/frappe/frappe/__init__.py", line 617, in msgprint
    _raise_exception()
  File "apps/frappe/frappe/__init__.py", line 568, in _raise_exception
    raise exc
frappe.exceptions.ValidationError: PDF generation failed because of broken image links

Email-queue Message:

Content-Type: multipart/mixed; boundary="===============2207987403685055338=="
MIME-Version: 1.0
Message-Id: <172882808235.8.12430365175257265920@test.menteware.com>
X-Original-From: Edward A <edward@menteware.com>
Subject: Re: Grant Plastics Ltd. (#ACC-SINV-2024-00001)
From: Edward A <test@menteware.com>
To: test@menteware.com
Date: Sun, 13 Oct 2024 14:01:22 -0000
Reply-To: test@menteware.com
X-Frappe-Site: https://test.menteware.com

--===============2207987403685055338==
Content-Type: multipart/alternative; boundary="===============7815968053194883997=="
MIME-Version: 1.0

--===============7815968053194883997==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable

otortorootr

 [View this in your browser](https://test.menteware.com/Sales
Invoice/ACC-SINV-2024-00001?format=3Dsi-demo&key=3Da933c8ce3ad49bcb7ac6169d=
b13aab4)


--===============7815968053194883997==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.=
w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=3D"http://www.w3.org/1999/xhtml">

<head>
	<meta name=3D"viewport" content=3D"width=3Ddevice-width">
	<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3DUTF-8">
	<title>Re: Grant Plastics Ltd. (#ACC-SINV-2024-00001)</title>
<style type=3D"text/css">@media only screen and (max-width: 700px) {
    .body-table.with-container .body-content {
        padding: 30px 20px !important
        }
    .body-table.with-container .email-container {
        padding: 20px !important;
        width: auto !important
        }
    }
.email-footer-container > div:not(:last-child) {margin-bottom:5px !importan=
t}
.table > thead:first-child > tr:first-child > th {border-top:none !importan=
t}
.ql-editor li > .ql-ui:before {display:inline-block !important;margin-left:=
-1.5em !important;margin-right:0.3em !important;text-align:right !important=
;white-space:nowrap !important;width:1.2em !important}
.ql-editor li[data-list=3Dbullet] > .ql-ui:before {content:"=E2=80=A2" !imp=
ortant}
.ql-editor li[data-list=3Dchecked] > .ql-ui:before {content:"=E2=98=91" !im=
portant}
.ql-editor li[data-list=3Dunchecked] > .ql-ui:before {content:"=E2=98=90" !=
important}
.ql-editor li[data-list=3Dordered] > .ql-ui:before {content:counter(list-0,=
 decimal) ". " !important}
.ql-editor li[data-list=3Dordered].ql-indent-1 > .ql-ui:before {content:cou=
nter(list-1, lower-alpha) ". " !important}
.ql-editor li[data-list=3Dordered].ql-indent-2 > .ql-ui:before {content:cou=
nter(list-2, lower-roman) ". " !important}
.ql-editor li[data-list=3Dordered].ql-indent-3 > .ql-ui:before {content:cou=
nter(list-3, decimal) ". " !important}
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {padding-left:3em !important}
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {padding-left:4.5em !impor=
tant}
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {padding-left:6em !important}
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {padding-left:7.5em !impor=
tant}
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {padding-left:9em !important}
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {padding-left:10.5em !impo=
rtant}</style>
</head>

<body style=3D"line-height:1.5; color:#1f272e">
	<table class=3D"body-table " cellpadding=3D"0" cellspacing=3D"0" style=3D"=
border-collapse:collapse; table-layout:fixed; margin:0 auto; border-spacing=
:0; font-weight:400; font-family:-apple-system, BlinkMacSystemFont, Segoe U=
I, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue=
, sans-serif; font-size:14px; line-height:1.4; height:100% !important; widt=
h:100% !important" height=3D"100% !important" width=3D"100% !important">
		<tr>
			<td class=3D"body-content" align=3D"center" valign=3D"top" style=3D"font=
-family:-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu=
, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; padding:20p=
x 0">
				<table class=3D"email-container" border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" width=3D" 100% ">
				=09
					<tr>
						<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSystem=
Font, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, H=
elvetica Neue, sans-serif">
						=09
						</td>
					</tr>
					<tr>
						<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSystem=
Font, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, H=
elvetica Neue, sans-serif">
							<table class=3D"email-body" border=3D"0" cellpadding=3D"0" cellspaci=
ng=3D"0" width=3D"100% !important" style=3D"font-size:14px; color:#1f272e; =
width:100% !important; min-width:100% !important">
								<tr>
									<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSys=
temFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans=
, Helvetica Neue, sans-serif">
										<p style=3D"margin:1em 0 !important"></p>
<div class=3D"ql-editor read-mode" style=3D"white-space:normal"><p style=3D=
"margin:0 !important">otortorootr</p></div>
<p style=3D"margin:1em 0 !important">
	<a href=3D"https://test.menteware.com/Sales%20Invoice/ACC-SINV-2024-00001?=
format=3Dsi-demo&amp;key=3Da933c8ce3ad49bcb7ac6169db13aab4" style=3D"color:=
#2d95f0">View this in your browser</a>
</p>
									</td>
								</tr>
							</table>
						</td>
					</tr>
					<tr>
						<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSystem=
Font, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, H=
elvetica Neue, sans-serif">
							<table class=3D"email-footer" border=3D"0" cellpadding=3D"0" cellspa=
cing=3D"0" width=3D"100% !important" style=3D"font-size:12px; line-height:2=
0px; width:100% !important; min-width:100% !important">
								<tr>
									<td valign=3D"top" data-email-footer=3D"true" style=3D"font-family=
:-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Canta=
rell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif">
										<div class=3D"email-footer-container text-muted" style=3D"margin-=
top:40px; color:#687178 !important">
	<!-- email_account_footer -->
=09

	<!-- sender_address -->
=09

	<!--unsubscribe link here-->

	<div class=3D"email-pixel">
		<!--email_open_check-->
	</div>

	<!-- default_mail_footer -->
=09
	<div class=3D"default-mail-footer">
	=09
		<div>
	<span>
		Sent via
		<a class=3D"text-muted" href=3D"https://erpnext.com?source=3Dvia_email_fo=
oter" target=3D"_blank" style=3D"color:#687178 !important">
			ERPNext
		</a>
	</span>
</div>
	=09
	</div>
=09

</div>
									</td>
								</tr>
								<tr>
									<td valign=3D"top" style=3D"font-family:-apple-system, BlinkMacSys=
temFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans=
, Helvetica Neue, sans-serif">
										<div class=3D"print-html"></div>
									</td>
								</tr>
							</table>
						</td>
					</tr>
				</table>
			</td>
		</tr>
	</table>
</body>

</html>

--===============7815968053194883997==--

--===============2207987403685055338==--

Execute this from worker-d

For extra_hosts

Hi @revant_one,

Could you please clarify where I should add the extra_hosts directive? All containers can access external sites and return the expected HTML when I run:

curl https://test.menteware.com

But I’m unsure why I might need extra_hosts in this scenario. Any guidance would be greatly appreciated!

Then image issue need more info. You can exec into the container and do bench --site site.name.com console then try to execute the python functions that are causing problems. Tweak code in console to understand why it triggers error.