Skip to main content

PD001: Override-Only Phantom

Severity: high  ·  Auto-fix: no

A package is imported directly in source code but is not declared in dependencies, devDependencies, peerDependencies, or optionalDependencies. It is only available at runtime because an override pin (overrides, resolutions, or pnpm.overrides) happens to place it in the resolved graph.

This is a deploy-breaking risk under pnpm strict mode (the default on Vercel and many CI environments). Strict mode does not hoist override-only packages, so the import fails at runtime even though npm install completed without errors.


Example

{
"overrides": {
"js-yaml": ">=4.2.0"
}
}
// src/config.ts
import yaml from "js-yaml";

js-yaml is not listed in dependencies or devDependencies. The override pin keeps it in the lockfile, and hoisting makes it importable in npm and non-strict Yarn setups - but the import silently breaks on Vercel, pnpm strict projects, or wherever hoisting is disabled.


Terminal output

HIGH (1)
┌───────┬─────────┬───────────────────────────────────────────────────────────────┐
│ Rule │ Package │ Message │
├───────┼─────────┼───────────────────────────────────────────────────────────────┤
│ PD001 │ js-yaml │ js-yaml is imported in source but only present via an │
│ │ │ override pin - declare it as a dependency │
│ │ │ package.json │
│ │ │ Imported in: src/config.ts. In pnpm strict mode (default on │
│ │ │ Vercel), override-only packages are not importable and will │
│ │ │ cause a deploy failure. Run: npm install js-yaml │
└───────┴─────────┴───────────────────────────────────────────────────────────────┘

Fix

PD001 has no auto-fix patch because declaring a dependency is an explicit intent decision, not a mechanical cleanup. Run the install command shown in the finding:

npm install js-yaml # npm
pnpm add js-yaml # pnpm
yarn add js-yaml # Yarn
bun add js-yaml # Bun

After declaring the dependency, the override pin may still be needed if you require a minimum safe version. Review the override entry after adding the declaration.


Why this is high severity

High because:

  1. Silent deploy failures. The import works locally (hoisting) but breaks in strict environments. The mismatch is nearly impossible to catch before it hits production.
  2. No lockfile warning. npm install, pnpm install, and yarn install all complete without error - the missing declaration is invisible until runtime.
  3. Scope is limited. PD001 only fires when source code actually imports the package. A package in overrides but not in source produces no finding.

Relationship to OA009

If a package triggers PD001, OA009 (Stale Floor) will not fire on the same override entry. OA009 recommends removing redundant override floors - but if the override is the only thing keeping the package available to source imports, removing it would break the import. The OA009 safety guard suppresses that finding when the package is undeclared.

Once you declare the package as a dependency (fixing PD001), the override floor may become eligible for OA009 cleanup if every parent's declared range already meets the floor.


Complementary rules

  • PD002 - same problem, but the package is a transitive dependency rather than an override-only package. Medium severity because transitive packages are at least present for all package managers, not just non-strict ones.
  • OA001 - flags override targets not present anywhere in the resolved tree at all.