{"record":{"id":"43e6bfe64e13f725","repo":"denoland/deno","slug":"invalid-doc-test-hashbang-bin-sh-binary-basen","errorCode":null,"errorMessage":"invalid doc test hashbang: #!/bin/sh (binary basename needs to be 'deno')","messagePattern":"invalid doc test hashbang: #!/bin/sh \\(binary basename needs to be 'deno'\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/util/extract.rs","lineNumber":2058,"sourceCode":"      },\n      // Unparseable shebang fails the test, naming the reason.\n      Test {\n        input: Input {\n          source: r#\"\n/**\n * ```ts\n * #!/bin/sh\n * foo();\n * ```\n */\nexport function foo() {}\n\"#,\n          specifier: \"file:///main.ts\",\n        },\n        expected: vec![Expected {\n          source: r#\"import { foo } from \"file:///main.ts\";\nDeno.test(\"file:///main.ts#3-7.ts\", async ()=>{\n    throw new Error(\"invalid doc test hashbang: #!/bin/sh (binary basename needs to be 'deno')\");\n    foo();\n});\n\"#,\n          specifier: \"file:///main.ts#3-7.ts\",\n          media_type: MediaType::TypeScript,\n        }],\n      },\n      // A scoped `--deny-*` can't be modelled, so the test is made to fail\n      // rather than run with broader permissions than the shebang declares.\n      Test {\n        input: Input {\n          source: r#\"\n/**\n * ```ts\n * #!/usr/bin/env -S deno run --allow-read --deny-read=/etc\n * foo();\n * ```\n */","sourceCodeStart":2040,"sourceCodeEnd":2076,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/cli/util/extract.rs#L2040-L2076","documentation":"With `deno test --doc`, every fenced code block in a doc comment becomes a generated Deno.test. If the fence starts with a shebang, parse_shebang() (cli/util/extract.rs:554) tokenizes it and searches the tokens for an executable whose file stem is exactly \"deno\". When none is found (e.g. #!/bin/sh), the extractor still emits the test but its body immediately throws Error(\"invalid doc test hashbang: ... (binary basename needs to be 'deno')\") so the example fails loudly instead of silently running with the wrong permissions.","triggerScenarios":"An exported function's JSDoc contains a ```ts fence whose first line is #!/bin/sh, #!/bin/bash, or #!/usr/bin/env node, and the test suite runs with deno test --doc.","commonSituations":"Documenting CLI wrapper scripts or shell-invocable examples inside library doc comments; copy-pasting runnable examples from README files that carry sh shebangs; CI doc-test jobs that start failing after such an example is added.","solutions":["Remove the shebang line from the fenced example - plain code needs none","Change it to a shebang the parser recognizes, e.g. #!/usr/bin/env -S deno run --allow-read=. (the binary basename must be exactly 'deno', not 'deno-canary')","Mark the fence to be skipped: ```ts ignore as the fence info string"],"exampleFix":"// before\n/**\n * ```ts\n * #!/bin/sh\n * foo();\n * ```\n */\n\n// after\n/**\n * ```ts\n * foo();\n * ```\n */\n// or keep the shebang example but skip it: ```ts ignore","handlingStrategy":"validation","validationCode":"// CI check: find shebangs inside doc comments that do not target a 'deno' binary\ngrep -rn --include='*.ts' -B2 '#!/bin/\\(sh\\|bash\\)\\|env node' src/ | grep -A2 '^.*\\* ```' || true","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep executable shebang examples out of ```ts doc fences; show them as ```sh instead","If a doc example must show a script header, use #!/usr/bin/env -S deno run ...","Mark non-runnable examples with ```ts ignore so deno test --doc skips them"],"tags":["testing","doc-tests","cli","shebang"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}