{"record":{"id":"695e3f925194d986","repo":"charmbracelet/crush","slug":"env-missing-program-name","errorCode":null,"errorMessage":"env: missing program name","messagePattern":"env: missing program name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/shell/dispatch.go","lineNumber":323,"sourceCode":"// isEnvShebang reports whether the shebang path targets `env`. We accept\n// both common absolute paths and a bare `env` so that unusual setups\n// (NixOS, BSDs) still work.\nfunc isEnvShebang(p string) bool {\n\tif p == \"/usr/bin/env\" || p == \"/bin/env\" {\n\t\treturn true\n\t}\n\treturn filepath.Base(p) == \"env\"\n}\n\n// parseEnvShebang handles `/usr/bin/env` rewriting. Without `-S`, the\n// remainder after the program name is a single argv[1] (kernel\n// single-arg semantics via env, even though real env would fail to find a\n// program named \"bash -x\"). With `-S`, the remainder is tokenized on\n// whitespace. Any other `env` flag is rejected — forwarding unknown flags\n// to a /usr/bin/env on disk is a subtle portability footgun we don't want.\nfunc parseEnvShebang(rest string) (*shebang, error) {\n\tif rest == \"\" {\n\t\treturn nil, errors.New(\"env: missing program name\")\n\t}\n\n\tuseSplit := false\n\tif strings.HasPrefix(rest, \"-\") {\n\t\tvar flag, after string\n\t\tif idx := strings.IndexAny(rest, \" \\t\"); idx >= 0 {\n\t\t\tflag = rest[:idx]\n\t\t\tafter = strings.TrimLeft(rest[idx+1:], \" \\t\")\n\t\t} else {\n\t\t\tflag = rest\n\t\t\tafter = \"\"\n\t\t}\n\t\tif flag != \"-S\" {\n\t\t\treturn nil, fmt.Errorf(\"unsupported env flag: %s\", flag)\n\t\t}\n\t\tuseSplit = true\n\t\trest = after\n\t\tif rest == \"\" {","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/shell/dispatch.go#L305-L341","documentation":"parseEnvShebang found an '#!/usr/bin/env' style shebang with no interpreter program named after env. env needs at least a program name to execute, so the parser rejects the line.","triggerScenarios":"A shebang like '#!/usr/bin/env' (nothing after), or '#!/usr/bin/env -S' where -S consumed the rest and left an empty program string.","commonSituations":"A truncated or hand-edited shebang line missing the interpreter (e.g. someone deleted 'python3' from '#!/usr/bin/env python3').","solutions":["Add the interpreter name after env: e.g. '#!/usr/bin/env bash' or '#!/usr/bin/env -S python3 -u'.","If using -S split-mode, make sure the program follows the flag on the same line.","Validate the script's first line before dispatching it in tooling."],"exampleFix":"// before\n#!/usr/bin/env\n// after\n#!/usr/bin/env bash","handlingStrategy":"validation","validationCode":"line, _ := bufio.NewReader(f).ReadString('\\n')\nrest := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), \"#!\"))\nif strings.HasPrefix(rest, \"#!/usr/bin/env\") && strings.TrimSpace(strings.TrimPrefix(rest, \"#!/usr/bin/env\")) == \"\" {\n    return errors.New(\"env shebang missing interpreter\")\n}","typeGuard":null,"tryCatchPattern":"if err := dispatch(script); err != nil {\n    if strings.Contains(err.Error(), \"missing program name\") {\n        return fmt.Errorf(\"%s: add an interpreter after /usr/bin/env\", script)\n    }\n    return err\n}","preventionTips":["Never write '#!/usr/bin/env' without a program name.","With -S, verify the interpreter follows the flag.","Lint shebangs in CI before shipping scripts."],"tags":["shell","shebang","env","malformed-input"],"backgroundTag":"invalid-shebang","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}