denoland/deno · error · Error

invalid doc test hashbang: #!/usr/bin/env -S deno run --allo

Error message

invalid doc test hashbang: #!/usr/bin/env -S deno run --allow-read --deny-read=/etc (scoped --deny-* flags aren't supported yet, either remove them or ignore the test)

What it means

Doc-test shebangs are parsed with Deno's own CLI flag parser and forwarded as the generated test's permission set. has_scoped_deny() (cli/util/extract.rs:588) reports true when any --deny-* flag carries a non-empty value list (deny_env/ffi/import/net/read/run/sys/write). Scoped denies like --deny-read=/etc cannot be expressed in the Deno.test permissions object, so the generated test throws this Error rather than running with broader permissions than the shebang declares.

Source

Thrown at cli/util/extract.rs:2084

      // A scoped `--deny-*` can't be modelled, so the test is made to fail
      // rather than run with broader permissions than the shebang declares.
      Test {
        input: Input {
          source: r#"
/**
 * ```ts
 * #!/usr/bin/env -S deno run --allow-read --deny-read=/etc
 * foo();
 * ```
 */
export function foo() {}
"#,
          specifier: "file:///main.ts",
        },
        expected: vec![Expected {
          source: r#"import { foo } from "file:///main.ts";
Deno.test("file:///main.ts#3-7.ts", async ()=>{
    throw new Error("invalid doc test hashbang: #!/usr/bin/env -S deno run --allow-read --deny-read=/etc (scoped --deny-* flags aren't supported yet, either remove them or ignore the test)");
    foo();
});
"#,
          specifier: "file:///main.ts#3-7.ts",
          media_type: MediaType::TypeScript,
        }],
      },
      // A bare `--deny-*` becomes `false`; only the flags present in the
      // shebang end up in the permissions object.
      Test {
        input: Input {
          source: r#"
/**
 * ```ts
 * #!/usr/bin/env -S deno run --allow-read --deny-env
 * foo();
 * ```
 */

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Remove the scoped --deny-*=... flag from the example's shebang
  2. Use a whole-flag deny instead, e.g. bare --deny-read (no value), which maps to permissions: { read: false } and is supported
  3. Mark the fence as ignored with ```ts ignore

Example fix

// before
/**
 * ```ts
 * #!/usr/bin/env -S deno run --allow-read --deny-read=/etc
 * foo();
 * ```
 */

// after
/**
 * ```ts
 * #!/usr/bin/env -S deno run --allow-read --deny-read
 * foo();
 * ```
 */
Defensive patterns

Strategy: validation

Validate before calling

// CI check: no scoped --deny-*=<value> flags in doc-test shebangs
grep -rn --include='*.ts' -- '--deny-[a-z]*=' src/ | grep '#!' || true

Prevention

When it happens

Trigger: A ```ts doc-test fence starting with #!/usr/bin/env -S deno run --allow-read --deny-read=/etc (any --deny-<perm>=<value> form) executed under deno test --doc.

Common situations: Documenting hardened scripts that exclude sensitive paths via scoped deny flags; copying a real-world shebang from a script into its doc comment; hardening examples added during a security pass that then break doc tests.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/6d998f853b0b980b. Report an issue: GitHub.