{"record":{"id":"f995321cdaaaebbe","repo":"dbt-labs/dbt-core","slug":"env-var-env-name-is-not-set-required-unless","errorCode":null,"errorMessage":"env var `{env_name}` is not set (required unless --dry-run)","messagePattern":"env var `(.+?)` is not set \\(required unless --dry-run\\)","errorType":"validation","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"crates/dbt-ci/src/homebrew/publish.rs","lineNumber":127,"sourceCode":"        eprintln!(\"→ dry-run: skipping push. Patch follows:\\n\");\n        run_git(Some(work.path()), &[\"--no-pager\", \"show\", \"HEAD\"])?;\n        return Ok(());\n    }\n\n    // Push needs the same `-c http.extraHeader=…` knobs as clone.\n    let mut push_argv: Vec<OsString> = auth_args;\n    push_argv.push(\"push\".into());\n    push_argv.push(\"origin\".into());\n    push_argv.push((&args.tap_branch).into());\n    run_git_os(Some(work.path()), &push_argv)?;\n    eprintln!(\"✓ pushed {filename} to {}\", args.tap_repo);\n    Ok(())\n}\n\nfn read_token(env_name: &str, dry_run: bool) -> Result<Option<String>> {\n    let v = env::var(env_name).ok().filter(|v| !v.is_empty());\n    if v.is_none() && !dry_run {\n        bail!(\"env var `{env_name}` is not set (required unless --dry-run)\");\n    }\n    Ok(v)\n}\n\n/// Builds `git -c http.extraHeader=Authorization: Basic <b64>` argv prefix\n/// for HTTPS URLs. Returns an empty Vec for non-HTTPS URLs (file://, ssh) or\n/// when no token is provided. The header is set via `-c` so it never enters\n/// the URL — `git remote -v` and clone logs stay clean.\n///\n/// GitHub's git HTTP backend accepts Basic auth, not Bearer. The\n/// `x-access-token` username is the convention `actions/checkout` uses\n/// internally and works for both user PATs and GitHub App tokens.\nfn build_auth_args(tap_url: &str, token: Option<&str>) -> Vec<OsString> {\n    let Some(token) = token else {\n        return Vec::new();\n    };\n    if !tap_url.starts_with(\"https://\") {\n        return Vec::new();","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-ci/src/homebrew/publish.rs#L109-L145","documentation":"The Homebrew publish step aborts when the required token environment variable (e.g. a GitHub token) is unset or empty and the command was not run with --dry-run. read_token() in crates/dbt-ci/src/homebrew/publish.rs only returns Some(token) when the env var holds a non-empty value; in non-dry-run mode a missing value is a hard bail because publishing cannot authenticate. The dry-run flag is the explicit opt-out for local testing.","triggerScenarios":"Running the homebrew publish command without --dry-run while the env var named by the config (e.g. HOMEBREW_GITHUB_TOKEN / GITHUB_TOKEN) is either not exported at all or exported as an empty string in the shell session that runs `run`.","commonSituations":"CI job that forgot to map the repository secret into the environment; running the publish command locally in a fresh shell where the token was only set in another profile; exporting the variable with `export FOO=` (empty) by mistake; a renamed env var after a workflow change.","solutions":["Export the required token env var with a non-empty value in the environment running the publish command (e.g. `export HOMEBREW_GITHUB_TOKEN=ghp_...`).","If this is a local test, re-run the command with --dry-run, which makes the token optional.","In CI, verify the secret is defined in the repository settings and passed to the job via `env:` mapping in the workflow file.","Check for typos between the env var name in the publish config and the name actually exported."],"exampleFix":"// before\n# .github/workflows/release.yml  (token never passed to the job)\n- run: dbt-ci homebrew publish ...\n\n// after\n- run: dbt-ci homebrew publish ...\n  env:\n    HOMEBREW_GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_TOKEN }}","handlingStrategy":"validation","validationCode":"import std::env;\nfn ensure_token(env_name: &str, dry_run: bool) -> Result<(), String> {\n    match env::var(env_name) {\n        Ok(v) if !v.is_empty() => Ok(()),\n        _ if dry_run => Ok(()),\n        _ => Err(format!(\"set {env_name} or pass --dry-run before invoking publish\")),\n    }\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"is not set (required unless --dry-run)\") => {\n        eprintln!(\"hint: export {} or use --dry-run\", env_name);\n    }\n    Err(e) => return Err(e),\n    Ok(_) => {}\n}","preventionTips":["Set the token in shell profiles or direnv so every session has it.","In CI, always map the secret into the job's env block.","Use --dry-run for local testing instead of exporting a fake token."],"tags":["env-var","missing-credentials","ci","homebrew","dry-run"],"backgroundTag":"missing-env-var","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}