denoland/deno · error · AnyError

Cannot generate declarations for files with different…

Error message

Cannot generate declarations for files with different compiler option scopes.
'{}' uses scope '{}' but '{}' uses scope '{}'.
Separate these files into different invocations.

What it means

Thrown by `deno cache` / declaration emit when the set of root files being type-checked in a single TSC pass do not all resolve to the same compiler-options scope. Deno maps every specifier to a compiler options entry (keyed by the effective tsconfig/deno.json settings that cover it, e.g. a workspace member's own compilerOptions). Because TSC type-checks all root names in one invocation, mixing scopes would produce wrong declarations, so Deno refuses and asks you to split the invocation.

Solutions

  1. Split the work: run a separate `deno cache` (or emit) invocation per scope, e.g. one per workspace member, so each invocation's root names share one scope.
  2. Align the scopes: make the conflicting deno.json / workspace-member `compilerOptions` identical (or delete the per-member override) so both files resolve to the same key.
  3. Identify the two scopes named in the message (first file's scope vs the offending file's scope) and move the offending file under the config that owns the first entrypoint.
  4. If you do not need declarations for the second file, drop it from the same invocation instead of mixing it in.

Example fix

# before: one invocation, two scopes
deno cache --config memberA/deno.json memberA/mod.ts memberB/mod.ts
# error: 'memberA/mod.ts' uses scope '...memberA/deno.json' but 'memberB/mod.ts' uses scope '...memberB/deno.json'

# after: one invocation per scope
deno cache --config memberA/deno.json memberA/mod.ts
deno cache --config memberB/deno.json memberB/mod.ts
Defensive patterns

Strategy: validation

Validate before calling

# group entrypoints by their deno.json/tsconfig scope before caching
# (each workspace member gets its own invocation)
for member in memberA memberB; do
  deno cache --config "$member/deno.json" "$member"/*.ts
done

Try / catch

deno cache main.ts 2>&1 | grep -q "different compiler option scopes" && { echo "split into per-scope invocations"; exit 1; }

Prevention

When it happens

Trigger: Running one command that emits declarations for multiple entrypoints where at least one file is covered by a different deno.json/tsconfig scope: e.g. a Deno workspace whose members declare differing `compilerOptions`, or entrypoints that resolve through node_modules/JSR packages carrying their own settings. The loop at cli/tools/transpile.rs compares each root_name's `entry_for_specifier()` key against the first file's key and bails on the first mismatch.

Common situations: Adopting Deno workspaces with per-member tsconfig overrides; adding a second entrypoint (test file, bench file) that sits in another workspace member; refactoring a monorepo so one import crosses a compilerOptions boundary; Deno version upgrades that changed how scopes are keyed.

Related errors


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

Appendix: source

Thrown at cli/tools/transpile.rs:278

      specifiers,
      crate::graph_util::BuildGraphWithNpmOptions {
        is_dynamic: false,
        loader: None,
        npm_caching: cli_options.default_npm_caching_strategy(),
      },
    )
    .await?;

  // Validate that all files share the same compiler options scope, since
  // TSC type-checks all files in a single pass.
  let compiler_options_resolver = factory.compiler_options_resolver()?;
  let first_specifier = &root_names[0].0;
  let (first_key, _) =
    compiler_options_resolver.entry_for_specifier(first_specifier);
  for (specifier, _) in root_names.iter().skip(1) {
    let (key, _) = compiler_options_resolver.entry_for_specifier(specifier);
    if key != first_key {
      anyhow::bail!(
        "Cannot generate declarations for files with different compiler option scopes.\n\
         '{}' uses scope '{}' but '{}' uses scope '{}'.\n\
         Separate these files into different invocations.",
        first_specifier,
        first_key,
        specifier,
        key,
      );
    }
  }

  let type_checker = factory.type_checker().await?;
  let result = type_checker.emit_declarations(
    Arc::new(graph),
    root_names,
    cli_options.ts_type_lib_window(),
  )?;

View on GitHub (pinned to 9ad36f7a2c)