denoland/deno · error · AnyError

Missing name

Error message

Missing name

What it means

`deno pack` derives the default tarball filename as `<name>-<version>.tgz` from deno.json's `name` field (default_tarball_filename is the single place this is computed). A config without `name` leaves nothing to derive the filename from, so pack fails before writing anything.

Solutions

  1. Add `"name"` and `"version"` to deno.json: `"name": "my-pkg", "version": "1.0.0"`
  2. For scoped packages use `"name": "@scope/my-pkg"` (normalized to scope-my-pkg)
  3. Run pack from the workspace member whose config actually defines the package

Example fix

// before (deno.json)
{ "tasks": { "dev": "deno run main.ts" } }

// after
{
  "name": "@scope/my-pkg",
  "version": "1.0.0",
  "tasks": { "dev": "deno run main.ts" }
}
Defensive patterns

Strategy: validation

Validate before calling

const config = JSON.parse(await Deno.readText('deno.json'));
if (typeof config.name !== 'string' || config.name.length === 0) {
  throw new Error('deno.json needs a "name" before deno pack');
}

Type guard

interface NamedConfig { name: string; version: string }
const isNamedConfig = (c: unknown): c is NamedConfig =>
  !!c && typeof (c as any).name === 'string' &&
  typeof (c as any).version === 'string';

Prevention

When it happens

Trigger: Running `deno pack` where deno.json has no `name` entry — e.g. a workspace root config that only holds tasks and imports, or a script project created by `deno init` without publish metadata.

Common situations: Workspace root configs used purely for task definitions; name/version defined only in a member config while running pack at the root; hand-rolled configs missing publish fields.

Related errors


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

Appendix: source

Thrown at cli/tools/pack/npm_tarball.rs:27

use super::AssetFile;
use super::ProcessedFile;
use super::ReadmeOrLicense;
use super::extensions::js_to_dts_extension;

/// Compute the default tarball filename (`scope-name-version.tgz`) that
/// `pack` writes when no `--output` is given. Extracted so the asset
/// collector can exclude this exact path (instead of guessing by `.tgz`
/// extension) and so the name is computed in exactly one place.
pub fn default_tarball_filename(
  config_file: &ConfigFile,
  version: &str,
) -> Result<PathBuf, AnyError> {
  let name = config_file
    .json
    .name
    .as_ref()
    .ok_or_else(|| deno_core::anyhow::anyhow!("Missing name"))?;
  // Convert @scope/name to scope-name
  let normalized = name.replace('@', "").replace('/', "-");
  // The package name shape is checked against `@scope/name` higher up
  // (see `pack` in mod.rs), but that check is loose — it does not
  // forbid path-traversal sequences. Treat this as a hard safety
  // boundary right before we open a file, rejecting any derived
  // tarball name that contains `..` or path separators so we never
  // escape the cwd regardless of upstream validation drift.
  if normalized.contains("..") || normalized.contains('/') {
    return Err(deno_core::anyhow::anyhow!(
      "refusing to write tarball with unsafe name derived from package: {}",
      name
    ));
  }
  Ok(PathBuf::from(format!("{}-{}.tgz", normalized, version)))
}

/// Tar archive paths must use forward slashes, even on Windows. Output paths

View on GitHub (pinned to 9ad36f7a2c)