Build1 publisher3 min readPublished
Writing P1M instead of P30D switches off Deno's dependency-age filter
Deno 2.6 added --min-dep-age to keep freshly published npm versions out of installs. Testing on 2.9.6 found that any duration carrying a month or year component drops the check entirely, with exit code 0 and an empty stderr.
The Engineer · Build desk

What happened
- Deno 2.6 shipped in December with a flag that tells deno install and deno add to refuse any dependency version published too recently, aimed at npm supply-chain attacks.
- Testing on Deno 2.9.6 found the guard works with a plain minute count, with day and week durations such as P30D and P4W, and with an absolute date, which forced zod back to 3.22.4.
- Any duration containing a month or year component, including zero-valued ones such as P0Y1M0D and P1Y0M0D, dropped the restriction and installed the actual latest version.
- Every failing run exited 0 with an empty stderr, and a debug-level log showed the resolver choosing a version without mentioning any skipped or rejected candidate.
- The equivalent deno.json key is minimumDependencyAge spelled out in full, and minDepAge, the key that would mirror the flag name, was silently ignored when tested.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure An organisation whose waiting period is naturally written in months has a control that enforces nothing, and the dev.to post says the only way to detect it is comparing installed versions against the registry by hand.
- constraint The safe configuration vocabulary shrinks to plain minutes, day or week durations and absolute dates, so a six-month policy has to be restated in days before it does any filtering.
- cost Adopting the guard costs you newer versions: a 30-day bar left vitest a whole major version back with nothing printed to explain the choice.
- decision Anyone shipping this in CI now has to decide whether a security control that stops enforcing on input it cannot parse belongs in the pipeline without an independent version check.
A thirty-day threshold and a one-month threshold are the same policy in any procurement document, but Deno resolves them differently. With `--min-dep-age=P30D`, `deno add` pinned zod to 4.4.3, published 135.9 days before the run [11]. Change the flag to `P1M` and it took 4.6.5, published 3.2 days before the run and the true latest at the time [12]. The gap between what the two policies installed is 132.7 days of publication age [2].
The accepted grammar turns out to be narrow. A single day or week component works, and testing published on dev.to found `P30D` and `P4W` both restricted the install, plain minutes worked, and `--min-dep-age=2024-01-01` forced zod back to 3.22.4 [14]. Everything else fails open. A month or year component drops the restriction even when its value is zero, so `P0Y1M0D` and `P1Y0M0D` both install the latest, and so does `P1DT0H` [15]. `P2Y` and `P100Y` behave the same as passing no flag at all [13].
The exit code is 0, stderr is empty, and at `--log-level=debug` the log shows the resolver picking a version and says nothing about a skipped or rejected candidate [16]. The help text documents the value as "The age in minutes, ISO-8601 duration or RFC3339 absolute timestamp" [2], and the examples it lists are day-based, such as `P2D`. It never says months and years are unsupported [19].
The runtime cost is small. The batch install of 20 packages took 10.2 seconds unrestricted and 10.6 seconds with the filter, one run each on a fresh Docker container with a cold cache [8]. The difference is 0.4 seconds, about 4 percent [3]. It transfers only if your install is dominated by the same registry metadata round trips, because the filter is a comparison against data npm already returns [9].
The cost the team sees is version drift. Twelve of the twenty commonly used packages resolved to an older version under the 30-day bar [5], 60 percent of the set [1]. Eight did not move, because their latest release was already more than 30 days old [6]. vitest went from 5.0.1, which was 1.8 days old at run time, back to 4.1.10, a full major version behind, with no error, warning or indication that a newer major existed [7].
The documented runs all use the command-line flag. The post states that the same policy written into `deno.json` as `"minimumDependencyAge": "P6M"` gives zero protection, with no way to notice short of comparing installed versions against the registry by hand [17]. It also reports that `minDepAge`, the config key that mirrors the flag name, is ignored without a warning [18].
In my view the only safe setting today is a threshold expressed in days, checked once after adoption against what the registry lists as latest.
What to watch
- Whether the Deno team treats the month and year fall-through as a parse bug and makes unrecognised durations an error instead of a no-op.
- Whether --min-dep-age leaves unstable status, and whether near-miss config keys such as minDepAge start producing a warning.
- Whether a second tester reproduces the P0Y1M0D and P1DT0H results on a later 2.9.x release.