denoland/deno · error · AnyError

missing version in package.json of npm package

Error message

missing version in package.json of npm package

What it means

Companion to 'unable to find npm package in workspace': the specifier DID match a workspace package.json by name, but that package.json has no top-level `version` field, so Deno cannot determine which version to unfurl the specifier to. Deno reads `pkg_json.version` and parses it with `Version::parse_from_npm`, requiring a plain semver string.

Solutions

  1. Add a plain semver `"version": "1.0.0"` (or the real version) to the matched package's package.json.
  2. Ensure the value is a bare semver version — no 'v' prefix, no range operators like '^'/'~' (Version::parse_from_npm rejects those).
  3. If the import was accidental (leftover from prototyping), remove the import or redirect it to the correct dependency instead of versioning a dummy package.

Example fix

// before (package.json)
{
  "name": "my-pkg",
  "exports": "./mod.ts"
}

// after
{
  "name": "my-pkg",
  "version": "1.0.0",
  "exports": "./mod.ts"
}
Defensive patterns

Strategy: validation

Validate before calling

const pkgJson = JSON.parse(await Deno.readText('package.json'));
if (typeof pkgJson.version !== 'string' || !/^\d+\.\d+\.\d+/.test(pkgJson.version)) {
  throw new Error('workspace npm package needs a plain semver "version" field');
}

Type guard

function hasSemverVersion(m: { version?: unknown }): boolean {
  return typeof m.version === 'string' && /^\d+\.\d+\.\d+(-[\w.-]+)?$/.test(m.version);
}

Try / catch

try {
  await runDeno(['publish']);
} catch (e) {
  if (String(e.message).includes('missing version in package.json')) {
    // add "version": "1.0.0" to the matching package.json, then retry
  }
}

Prevention

When it happens

Trigger: A local workspace npm package whose package.json omits `version` (common for private app crates where version is irrelevant); a version field set to a non-semver value would instead fail parsing; the package is imported by a JSR package being published, triggering the lookup.

Common situations: Monorepos mixing an npm package (never published, no version) with a JSR package that imports it; templates generated without version; users surprised that internal-use packages still need version when referenced from publishable code.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/2abb73c81cbf65ae. Report an issue: GitHub.

Appendix: source

Thrown at cli/tools/publish/unfurl.rs:680

  }

  fn find_workspace_npm_dep_version(
    &self,
    pkg_name: &str,
  ) -> Result<Version, anyhow::Error> {
    // todo(#24612): warn when this is also a jsr package telling
    // people to map the specifiers in the import map
    let pkg_json = self
      .workspace_resolver
      .package_jsons()
      .find(|pkg| pkg.name.as_deref() == Some(pkg_name))
      .ok_or_else(|| {
        anyhow::anyhow!("unable to find npm package in workspace")
      })?;
    if let Some(version) = &pkg_json.version {
      Ok(Version::parse_from_npm(version)?)
    } else {
      Err(anyhow::anyhow!(
        "missing version in package.json of npm package",
      ))
    }
  }

  /// Look up the version constraint for a @types/* package from package.json
  /// dependencies (including devDependencies) or from the import map.
  fn find_types_package_version_req(
    &self,
    types_package_name: &str,
    referrer: &ModuleSpecifier,
  ) -> Option<VersionReq> {
    // check package.json dependencies
    let referrer_path = deno_path_util::url_to_file_path(referrer).ok();
    let referrer_pkg_jsons = referrer_path
      .as_ref()
      .map(|path| self.pkg_json_resolver.get_closest_package_jsons(path))
      .into_iter()

View on GitHub (pinned to 9ad36f7a2c)