{"record":{"id":"6274d7dc76a2f6a5","repo":"charmbracelet/crush","slug":"unsupported-env-flag-s","errorCode":null,"errorMessage":"unsupported env flag: %s","messagePattern":"unsupported env flag: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/shell/dispatch.go","lineNumber":337,"sourceCode":"// 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 == \"\" {\n\t\t\treturn nil, errors.New(\"env -S requires a program\")\n\t\t}\n\t}\n\n\tif rest == \"\" {\n\t\treturn nil, errors.New(\"env: missing program name\")\n\t}\n\n\tvar prog, remainder string\n\tif idx := strings.IndexAny(rest, \" \\t\"); idx >= 0 {\n\t\tprog = rest[:idx]\n\t\tremainder = strings.TrimLeft(rest[idx+1:], \" \\t\")\n\t} else {\n\t\tprog = rest","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/shell/dispatch.go#L319-L355","documentation":"parseEnvShebang rejects `env` shebangs that use any flag other than -S (split-string). The dispatcher implements env rewriting itself rather than forwarding to a real /usr/bin/env, and deliberately refuses unknown flags because forwarding them to a real env binary would be a portability footgun.","triggerScenarios":"A script's shebang is an env form whose first token after `env` starts with '-' but is not exactly \"-S\", e.g. `#!/usr/bin/env -i bash`, `#!/usr/bin/env -P /opt/bin python`, or `#!/usr/bin/env --split-string bash`.","commonSituations":"Portable scripts using `env -i` for a clean environment; BSD/macOS scripts using `env -P` to override PATH lookup; scripts using the long form `--split-string`; NixOS wrappers emitting extra env flags.","solutions":["Replace the env flag with plain env form: `#!/usr/bin/env <interpreter>`.","If you need a clean environment, unset variables in the script body instead of using `env -i`.","Use `#!/usr/bin/env -S <interpreter> <args>` if the goal was argument splitting; only -S is supported.","If the interpreter needs a custom PATH, export PATH at the top of the script rather than via env -P."],"exampleFix":"// before\n#!/usr/bin/env -i bash\nset -e\n// after\n#!/usr/bin/env bash\nset -e","handlingStrategy":"validation","validationCode":"// reject env flags other than -S in shebangs before shipping\nfor _, line := range lines {\n    if strings.HasPrefix(line, \"#!\") && strings.Contains(line, \"env -\") && !strings.Contains(line, \"env -S \") {\n        return fmt.Errorf(\"unsupported env flag in shebang: %s\", line)\n    }\n}","typeGuard":null,"tryCatchPattern":"var unsupported bool\nif strings.HasPrefix(err.Error(), \"unsupported env flag:\") {\n    unsupported = true\n}","preventionTips":["Only use `#!/usr/bin/env -S ...` or plain `#!/usr/bin/env ...` shebangs.","Replace env -i/-P patterns with in-script PATH/export handling.","Add a repo lint rule rejecting env flags other than -S.","Test scripts through the dispatcher, not just the kernel."],"tags":["shell","shebang","env","parsing"],"backgroundTag":"unsupported-env-shebang-flag","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}