Repository navigation
Website og:image and twitter:image use a data URI prefixed with site-url, ignoring website.image #15055
Description
I have:
- searched the issue tracker for similar issues
- installed the latest version of Quarto CLI
- formatted my issue following the Bug Reports guide
Bug description
In a website project, Quarto uses the first image on a page for og:image and twitter:image when the page doesn't set image.
If that first image is a data URI, Quarto still picks it, and prefixes it with site-url.
The tag ends up as https://example.com/data:image/png;base64,..., which no social platform can fetch.
The site-wide website.image would have been a valid fallback, but the data URI wins over it.
Authors can hit this without writing an image at all.
When the author block has an orcid, Quarto renders the ORCID badge as a 16 by 16 PNG data URI, and that badge is the first image on the page.
Two steps in the code lead to this.
findPreviewImgEl returns the first image whose src starts with data:, in the same pass that looks for feature, cover and thumbnail file names:
quarto-cli/src/project/types/website/util/discover-meta.ts
Lines 65 to 75 in b86f98d
| // look for an image with name feature, cover, or thumbnail | |
| const imgs = doc.querySelectorAll("img"); | |
| for (let i = 0; i < imgs.length; i++) { | |
| const img = imgs[i] as Element; | |
| const src = getDecodedAttribute(img, "src"); | |
| if ( | |
| src !== null && (src.startsWith("data:") || kNamedFileRegex.test(src)) | |
| ) { | |
| return img; | |
| } | |
| } |
imageMetadata then treats the value as a relative path and joins it to site-url, because isAbsoluteRef only recognises http:// and https://:
quarto-cli/src/project/types/website/website-meta.ts
Lines 427 to 468 in b86f98d
| function imageMetadata( | |
| image: string, | |
| baseUrl: string, | |
| source: string, | |
| context: ProjectContext, | |
| ) { | |
| if (isAbsoluteRef(image)) { | |
| // We can't resolve any size data, just return the image | |
| return { | |
| href: image, | |
| }; | |
| } else if (image.startsWith("/")) { | |
| // This is a project relative path | |
| const imagePath = join(context.dir, image.slice(1)); | |
| const size = imageSize(imagePath); | |
| // read the image size | |
| return { | |
| path: image, | |
| href: joinUrl(baseUrl, image), | |
| height: size?.height, | |
| width: size?.width, | |
| }; | |
| } else { | |
| // This is an input relative path | |
| const sourceRelative = relative(context.dir, source); | |
| const imageProjectRelative = join( | |
| dirname(sourceRelative), | |
| image, | |
| ); | |
| const imagePath = join(context.dir, imageProjectRelative); | |
| const size = imageSize(imagePath); | |
| // resolve the image path into an absolute href | |
| return { | |
| path: imageProjectRelative, | |
| href: joinUrl(baseUrl, imageProjectRelative), | |
| height: size?.height, | |
| width: size?.width, | |
| }; | |
| } | |
| } |
Lines 24 to 26 in b86f98d
| export function isAbsoluteRef(href: string) { | |
| return /^(?:http|https)\:\/\/.+/.test(href); | |
| } |
On main, the render now fails instead.
Since #15050, imageSize checks every path on disk rather than only .png paths, so the data URI is passed to existsSync as a file name.
On macOS, this stops the render with File name too long (os error 63).
Steps to reproduce
_quarto.yml:
project:
type: website
website:
title: "OG repro"
site-url: https://example.com
image: site.png
open-graph: true
twitter-card: trueindex.qmd:
---
title: "Home"
author:
- name: Jane Doe
orcid: 0000-0002-0000-0000
---
Some text.other.qmd:
---
title: "Other"
---
Some text.Add any PNG as site.png at the project root, then run quarto render.
Actual behavior
With 1.9.38, 1.10.18, 1.10.19 and 1.11.6, _site/index.html has:
<meta property="og:image" content="https://example.com/data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQ...">
<meta name="twitter:image" content="https://example.com/data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQ...">_site/other.html correctly has https://example.com/site.png, with its width and height.
With main at b86f98d, the render fails on index.qmd:
ERROR: File name too long (os error 63): stat '<project>/data:image/png;base64,iVBORw0KGgo...'
Stack trace:
at Object.statSync (ext:deno_fs/30_fs.js:432:3)
at existsSync (https://jsr.io/@std/fs/1.0.16/exists.ts:268:23)
at imageSize (src/core/image.ts:16:9)
at imageMetadata (src/project/types/website/website-meta.ts:458:18)
at resolveImageMetadata (src/project/types/website/website-meta.ts:358:21)
Expected behavior
Quarto skips data URIs when it looks for a preview image, because social platforms can't fetch them.
index.html then falls back to website.image, as other.html does.
If a data URI does end up in image, Quarto leaves it as it is: no site-url prefix, and no attempt to read its size from disk.
Your environment
- IDE: none, command line.
- OS: macOS 27.0 (Apple Silicon).
Quarto check output
Quarto 1.11.6
[✓] Checking environment information...
Quarto cache location: /Users/mcanouil/Library/Caches/quarto
Project: none found (single-file mode)
[✓] Checking versions of quarto binary dependencies...
Pandoc version 3.10.0: OK
Dart Sass version 1.101.0: OK
Deno version 2.7.14: OK
Typst version 0.15.1: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
Version: 1.11.6
Path: /private/tmp/claude-501/-Users-mcanouil-Projects-quarto-extensions/93418f4b-eb51-487a-b7a8-d736f6b97ca2/scratchpad/bins/1.11.6/bin
[✓] Checking tools....................OK
TinyTeX: v2026.07
Chrome Headless Shell: (not installed)
VeraPDF: (not installed)
[✓] Checking LaTeX....................OK
Using: TinyTex
Path: /Users/mcanouil/Library/TinyTeX/bin/universal-darwin
Version: 2026
[✓] Checking Chrome Headless....................OK
Using: Chrome from QUARTO_CHROMIUM
Path: /Applications/Brave Browser.app/Contents/MacOS/Brave Browser
[✓] Checking basic markdown render....OK
ℹ R version 4.6.1 (2026-06-24)
! Config '~/.Rprofile' was loaded!
[✓] Checking R installation...........OK
Version: 4.6.1
Path: /Library/Frameworks/R.framework/Versions/4.6/Resources
LibPaths:
- /Users/mcanouil/Library/R/arm64/4.6/library
- /Library/Frameworks/R.framework/Versions/4.6/Resources/library
knitr: 1.51
rmarkdown: 2.31
[✓] Checking Knitr engine render......OK
[✓] Checking Python 3 installation....OK
Version: 3.9.6
Path: /Library/Developer/CommandLineTools/usr/bin/python3
Jupyter: (None)
Jupyter is not available in this Python installation.
Install with python3 -m pip install jupyter
[✓] Checking Julia installation...
Activity
Skip data URI images in social metadata and listing feeds (quarto-dev…