{"record":{"id":"bc472a0a8e25c389","repo":"JuliusBrussee/caveman","slug":"s-is-required","errorCode":null,"errorMessage":"%s is required","messagePattern":"(.+?) is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/env/env.go","lineNumber":48,"sourceCode":"\treturn parsed\n}\n\nfunc Int(name string, fallback int) int {\n\tv, ok := os.LookupEnv(name)\n\tif !ok || strings.TrimSpace(v) == \"\" {\n\t\treturn fallback\n\t}\n\tparsed, err := strconv.Atoi(strings.TrimSpace(v))\n\tif err != nil {\n\t\treturn fallback\n\t}\n\treturn parsed\n}\n\nfunc Required(name string) (string, error) {\n\tv := strings.TrimSpace(os.Getenv(name))\n\tif v == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%s is required\", name)\n\t}\n\treturn v, nil\n}\n\n// IsProduction is the single definition of \"this process runs in production\".\n// Every refusal gate must agree on it: a CAVE_ENV of \" prod\" or \"PROD\" that one\n// check treated as production and another as local would arm half the production\n// refusals and silently skip the rest. Whitespace and case are therefore folded\n// in — the direction that turns MORE deployments on, never fewer.\nfunc IsProduction() bool {\n\treturn runtimeenv.IsProduction()\n}\n\nfunc RefuseProductionDefaults() error {\n\tif !IsProduction() {\n\t\treturn nil\n\t}\n\tif err := validateProductionTextSecrets([]string{","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/env/env.go#L30-L66","documentation":"The only error in shared/platform/env/env.go Required(): the named environment variable is unset, empty, or whitespace-only after strings.TrimSpace. It is the library's generic 'mandatory configuration missing' signal and carries the variable name in the message.","triggerScenarios":"Calling env.Required(\"SOME_VAR\") in a process where SOME_VAR is not exported, is set to an empty string, or holds only spaces/tabs. The trimmed value is what is tested, so ' ' fails exactly like ''.","commonSituations":"A .env file not loaded in the deployed environment; secret injected under a slightly different name (CAVE_JWT_SIGNING_KEY vs JWT_KEY); variable commented out in the compose file; CI job missing an env block; trailing spaces around '=' in an env file creating a name with a space.","solutions":["Export the variable with a non-empty value in the environment that runs the process.","Check for name mismatches: compare the exact name in the error message against what your deployment injects.","If using dotenv files, confirm they are loaded before the call and the line has no stray whitespace around the key.","For optional variables, use env.String(name, fallback) instead of Required."],"exampleFix":"# before\n# CAVE_BOOTSTRAP_TOKEN=  (empty)\n\n# after\nCAVE_BOOTSTRAP_TOKEN=<32+ char token>","handlingStrategy":"try-catch","validationCode":"for _, name := range []string{\"CAVE_KEY_HASH_PEPPER\", \"CAVE_JWT_SIGNING_KEY\"} {\n    if strings.TrimSpace(os.Getenv(name)) == \"\" {\n        return fmt.Errorf(\"missing required env %s\", name)\n    }\n}","typeGuard":null,"tryCatchPattern":"v, err := env.Required(\"CAVE_BOOTSTRAP_TOKEN\")\nif err != nil {\n    return fmt.Errorf(\"configuration incomplete: %w\", err) // name is already in the message\n}","preventionTips":["Declare every Required() variable in one init checklist and diff it against the deployment manifest.","Fail at startup, not lazily at first use, so missing env is a boot error.","Prefer explicit empty-string checks in templates over relying on later failures."],"tags":["go","config","environment","env-vars"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}