Repository navigation
Update bundled Deno from 2.7.14 to 2.9.x #15157
Description
Quarto 1.11 moves the bundled Deno from 2.7.14 to the 2.9 line (2.9.8 at time of writing). This issue tracks the update and every Deno-related change it requires, so we can decide later what needs to be backported to 1.10.x.
The main reason for doing it now is security. Deno 2.9.8 distrusts the compromised eMudhra / emSign intermediates (denoland/deno#37046), and 2.7.14 still accepts certificates chaining to them. Details in #15152. Any 2.9.x before 2.9.8 does not have the fix, so 2.9.8 is the minimum.
Changes required by the update
deno installrefuses permission flags outside a global install (-g) since Deno's CLI parser rewrite (refactor: remove clap from the tree denoland/deno#36489). Our vendoring scripts calldeno install --allow-all --entrypoint ..., which 2.7.14 accepts and 2.9.8 rejects withPermission flags can only be used in a global setting. The scripts continue on error, soconfigurefinishes but no dependency is vendored and thedeno_cachestays empty. Passing--allow-importinstead, which is the only permission flag the non-global form still accepts, vendors all four entrypoints.
$DENO_BIN_PATH install --allow-all --no-config --entrypoint $entrypoint "--importmap=$QUARTO_SRC_PATH/import_map.json"
CALL !DENO_BIN_PATH! install --allow-all --no-config --entrypoint %%E --importmap="!QUARTO_SRC_PATH!\import_map.json" || ( - Deno 2.9.8 bundles TypeScript 6.0, which reports new type errors in our code. As
deno testtype-checks each test's import graph, they fail the whole test suite. Three kinds: import assertions (assert: { type: "json" }) must become import attributes (with),fetchno longer accepts aUint8Array<ArrayBufferLike>body (Posit Connect Cloud publisher), andsetTimeoutis now typed as returning Node'sTimeoutinstead ofnumber(two tests). The fixes also type-check with Deno 2.7.14. - conda-forge has no Deno 2.9.8 build yet (latest is 2.9.5), so the RHEL tarball job stays disabled, see Add back RHEL7 build when Deno 2.4.5 is available in conda-forge (or not) #13409.
I'll add here any other Deno-related change found during the update or after it ships.
Backport
The security fix only matters for 1.10.x if 1.10.x also gets Deno 2.9.8. In that case, the vendoring script change and the type fixes above need to go with it.
Activity
cderv commentedon Oct 22, 2026
Found a blocking problem with this update: Deno KV values written by Deno 2.9.8 can't be read by Deno 2.7.14, reading them fails with
RangeError: could not deserialize value. The other direction works, 2.9.8 reads data written by 2.7.14.Quarto uses Deno KV for two caches:
.quarto/project-cache/quarto-cli/src/core/cache/cache.ts
Lines 68 to 72 in 517944d
%LOCALAPPDATA%\quarto\sasson Windows,~/Library/Caches/quarto/sasson macOS,~/.cache/quarto/sasson Linux), which is shared by every Quarto version installed on the machinequarto-cli/src/core/sass.ts
Lines 409 to 411 in 517944d
I tested on Windows with a dev build on Deno 2.9.8 and Quarto 1.10.19 (Deno 2.7.14):
ERROR: RangeError: could not deserialize value..quarto/or thesasscache folder fixes it.So as soon as a Quarto with Deno 2.9.8 has rendered on a machine, an older Quarto on the same machine (for example the one bundled with RStudio or Positron) fails to render projects, and HTML documents through the Sass cache, until the cache is deleted. Same for a 1.10.x backport: older 1.10.x installs would break once the patch release has run. The
QUARTO_DENOworkaround from #15152 does it too: 1.10.19 withQUARTO_DENOpointing to Deno 2.9.8, then 1.10.19 without it, gives the same error.This needs a solution before we ship Deno 2.9.8. We could store the KV files under a name or folder that depends on the Deno version, so older versions never open them. Treating a deserialize error as a cache miss would only help versions that have this fix, not the ones already released.
Deno only reproduction
deno-2.9.8 run --unstable-kv -A write.ts kv-file deno-2.7.14 run --unstable-kv -A read.ts kv-file # RangeError: could not deserialize valueKeep Deno KV caches per V8 release branch to handle several Deno (#15180
cderv commentedon Oct 23, 2026
The KV cache problem from my previous comment is fixed by #15180, which keeps the KV stores in a folder per V8 version. I checked it with the update branch rebased on it and Quarto 1.10.19, rendering in both orders, for a project and for a single document. All renders work, and each version keeps its own cache.
Going through the Deno 2.8 and 2.9 changes, I found three other behaviour changes that affect us.
Network errors lose their reason
Since denoland/deno#35618, a failed
fetchthrowsTypeError: fetch failed, and the actual reason is only inerror.cause. We only printerror.message, so users don't see the host or the reason anymore. For example withquarto add https://expired.badssl.com/x.zip:quarto-cli/src/core/log.ts
Lines 360 to 366 in 714c426
This matters for this update in particular: a user behind a server using the distrusted eMudhra intermediates would get a certificate error, and only see
fetch failed. We could printcauseinlogError.Port 4190 is now blocked by fetch
Deno 2.9 blocks the Fetch standard bad ports (denoland/deno#34863). Our
isPortSafelist has 4160 for sieve, but the sieve port in the Fetch list is 4190, so our random port picker can still return 4190.quarto-cli/src/core/port.ts
Lines 17 to 37 in 714c426
Chrome uses 9222 when it is free, otherwise a random port between 3000 and 8000. If that port is 4190, the readiness check
fetchon it will always fail, until the launch times out.quarto-cli/src/core/cri/launch.ts
Line 253 in 714c426
quarto-cli/src/core/cri/launch.ts
Line 170 in 714c426
With a server listening on 4190,
fetch("http://localhost:4190/json/list")returns 200 with Deno 2.7.14, and fails withRequests to port 4190 are blockedwith Deno 2.9.8. I did not reproduce it with Chrome itself. All the other bad ports between 3000 and 8000 are already in our list.quarto runkeeps deleting its Deno cacheBefore running a script,
quarto runchecks that every file of the bundleddeno_stdcache is also in the user cache, and if not it deletes the user cache and copies it again.quarto-cli/src/core/run/deno.ts
Lines 109 to 116 in 714c426
The bundled cache has SQLite
-shmand-walfiles, and Deno 2.9.8 removesnode_analysis_cache_v2-shmafter a run. So the nextquarto rundeletes the cache, including the npm packages the script downloaded. WithQUARTO_RUN_NO_NETWORK=true, a script importing annpm:package fails right after an online run:Quarto 1.10.19 keeps its cache and runs the same script offline fine. Deno 2.9.8 alone, with its own
DENO_DIR, also works offline, so this is the filename comparison in our code meeting a different SQLite cleanup in the new Deno.These three come with the Deno update, so they'll need to go with it, including in a 1.10.x backport if we do one. I am still checking the remaining items (test sanitizers now off by default in
deno test,deno bundleforquarto call build-ts-extension, PDF test helpers, preview).