{"record":{"id":"afb6b2257c3eea8c","repo":"astrid-runtime/astrid","slug":"required-environment-variable-astrid-hook-token-is","errorCode":null,"errorMessage":"required environment variable ASTRID_HOOK_TOKEN is missing or empty","messagePattern":"required environment variable ASTRID_HOOK_TOKEN is missing or empty","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/astrid-emit/src/lib.rs","lineNumber":353,"sourceCode":"    })\n}\n\n/// Process-environment lookup: a variable counts as present only if it is\n/// set **and** non-empty.\nfn env_lookup(name: &str) -> Option<String> {\n    std::env::var(name).ok().filter(|v| !v.is_empty())\n}\n\n/// Read the hook token supplied by the host runner.\n///\n/// The runner mints and persists this token once per session. Hook events only\n/// echo it; this helper deliberately never generates a replacement token.\n///\n/// # Errors\n/// Returns an error when `ASTRID_HOOK_TOKEN` is missing or empty.\npub fn hook_token_from_env() -> Result<String> {\n    env_lookup(\"ASTRID_HOOK_TOKEN\").ok_or_else(|| {\n        anyhow::anyhow!(\"required environment variable ASTRID_HOOK_TOKEN is missing or empty\")\n    })\n}\n\n/// Core, testable logic: given a topic, stdin payload, the three env\n/// values, and an [`Emitter`], build the envelope and publish it.\n///\n/// The caller (`run`) is responsible for writing the\n/// `{\"continue\":true}` stdout line on **every** path — this function\n/// only decides stderr + exit code. It never returns an exit code other\n/// than `0` or `1`.\npub async fn emit<E: Emitter>(\n    emitter: &E,\n    topic: &str,\n    stdin_payload: &str,\n    env: &HookEnvValues<'_>,\n) -> Outcome {\n    let hook = derive_hook(topic);\n    let envelope = build_envelope(","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-emit/src/lib.rs#L335-L371","documentation":"`hook_token_from_env` reads `ASTRID_HOOK_TOKEN` via `env_lookup` and deliberately never generates a replacement token — the emitting process must echo the token the host provided so hooks are authenticated. If the variable is missing or empty, publishing a hook envelope is refused with this error.","triggerScenarios":"Calling `hook_token_from_env()` (or the emit path that uses it) in a process where `ASTRID_HOOK_TOKEN` is not set or is the empty string.","commonSituations":"Running a capsule/hook binary manually outside the host (which normally injects the token); shell environments that strip the variable; launching the hook from cron/systemd without inheriting the parent environment.","solutions":["Run the hook through the normal astrid host launch path so it injects `ASTRID_HOOK_TOKEN`.","If manual invocation is required, copy the token value the host exposed into the environment: `ASTRID_HOOK_TOKEN=<token> <command>`.","Check for empty-string values (`printenv ASTRID_HOOK_TOKEN`) — empty counts as missing; re-export a real token."],"exampleFix":"// before\n$ astrid-emit my-topic < payload.json\n// after\n$ export ASTRID_HOOK_TOKEN=\"$TOKEN_FROM_HOST\"\n$ astrid-emit my-topic < payload.json","handlingStrategy":"validation","validationCode":"// shell\n[ -n \"$ASTRID_HOOK_TOKEN\" ] || { echo \"ASTRID_HOOK_TOKEN must be set (host-injected)\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":"// rust\nlet token = match astrid_emit::hook_token_from_env() {\n    Ok(t) => t,\n    Err(e) => { eprintln!(\"launch via the astrid host so ASTRID_HOOK_TOKEN is injected: {e}\"); std::process::exit(1); }\n};","preventionTips":["Always launch hooks/emitters through the astrid host, which injects the token.","Use `printenv ASTRID_HOOK_TOKEN` to verify non-empty values before manual runs.","For cron/systemd, export the token in the unit or wrapper environment."],"tags":["environment","authentication","hooks"],"backgroundTag":"missing-env-var","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}