denoland/deno · error
{hint}
Error message
{hint} What it means
During `deno add`, `link_hint_for_spec` detects arguments that are path-shaped (., .., ./x, containing separators) and point at an existing local directory whose deno.json(c) declares a `name` — i.e. a linkable local JSR package. Adding local directories as registry deps is not supported, so Deno stops you and suggests `deno link` in the message.
Source
Thrown at cli/tools/pm/mod.rs:615
let deps_file_fetcher = Arc::new(deps_file_fetcher);
let jsr_resolver = Arc::new(JsrFetchResolver::new(
deps_file_fetcher.clone(),
cli_factory.jsr_version_resolver()?.clone(),
));
let npm_resolver = Arc::new(NpmFetchResolver::new(
deps_file_fetcher,
npmrc.clone(),
cli_factory.npm_version_resolver()?.clone(),
));
let mut selected_packages = Vec::with_capacity(add_flags.packages.len());
let mut package_reqs: Vec<AddRmPackageReq> =
Vec::with_capacity(add_flags.packages.len());
let initial_cwd = cli_factory.cli_options()?.initial_cwd().to_path_buf();
for entry_text in add_flags.packages.iter() {
if let Some(hint) = link_hint_for_spec(&initial_cwd, entry_text) {
bail!("{hint}");
}
let req = AddRmPackageReq::parse(
entry_text,
add_flags.default_registry.map(|r| r.into()),
)
.with_context(|| format!("Failed to parse package: {}", entry_text))?;
match req {
Ok(mut add_req) => {
if add_flags.unscoped {
add_req.use_unscoped_alias();
// packages from different scopes can share an unscoped name
// (ex. `@luca/flag` and `@other/flag`), which would otherwise
// silently overwrite each other in the config
if let Some(existing) =
package_reqs.iter().find(|r| r.alias == add_req.alias)
{
bail!(View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Use `deno link ./packages/my-lib` as the message suggests (wires the local package into the workspace).
- If the path was accidental, fix the specifier to a real registry package (e.g. `npm:chalk`).
- For local-only development without linking, add a workspace member entry or a relative import instead of `deno add`.
Example fix
# before deno add ./packages/my-lib # after deno link ./packages/my-lib
Defensive patterns
Strategy: validation
Validate before calling
import { statSync, readFileSync, isAbsolute, resolve } from "node:fs";
import { join } from "node:path";
const spec = process.argv[2] ?? "";
const looksPathy = spec === "." || spec === ".." || spec.includes("/") || spec.includes("\\") || isAbsolute(spec);
if (looksPathy) {
const abs = resolve(spec);
try {
if (statSync(abs).isDirectory()) {
const target = JSON.parse(readFileSync(join(abs, "deno.json"), "utf8"));
if (target.name) {
console.error(`${spec} is a local package; use: deno link ${spec}`);
process.exit(1);
}
}
} catch { /* not a linkable package dir */ }
} Prevention
- Use `deno link <path>` for local JSR packages; `deno add` is for registry packages.
- Detect path-shaped arguments in wrappers and route them to link.
- Workspace members with names are link targets; bare folders are not.
When it happens
Trigger: `deno add ./packages/my-lib` (or ../other, /abs/path) where that directory exists and its deno.json has a `name` field. Bare names like `deno add chalk` never trigger it; non-existent paths are passed through to normal parsing.
Common situations: Monorepos where sibling packages are consumed locally; users expecting npm-style `file:` installs; refactoring a workspace member into consumption via add.
Related errors
- Bench attempted to exit with exit code: ${exitCode}
- Missing 'name' field in config file.
- Did not format non-workspace directory. Run again specifying
- Failed to install "{}" specifier. If you are trying to insta
- {} and {} would both be added as "{}". Provide an explicit a
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/974fab3dd7c9c04b.
Report an issue: GitHub.