{"record":{"id":"d4d137c843f453f1","repo":"slopus/happy","slug":"session-environment-is-invalid-environment-varia","errorCode":null,"errorMessage":"Session environment is invalid - environment variables not found in daemon: ${unresolvedEnvEntries.join('; ')}. Ensure these variables are set in the daemon's environment before starting sessions.","messagePattern":"Session environment is invalid - environment variables not found in daemon: (.+?)\\. Ensure these variables are set in the daemon's environment before starting sessions\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/daemon/run.ts","lineNumber":391,"sourceCode":"\n          const unresolvedMatch = value.match(/\\$\\{([^}]+)\\}/);\n          if (!unresolvedMatch) {\n            return [];\n          }\n\n          const expression = unresolvedMatch[1];\n          const defaultSeparatorIndex = expression.indexOf(':-');\n          const missingVar = defaultSeparatorIndex === -1\n            ? expression\n            : expression.slice(0, defaultSeparatorIndex);\n\n          return [`${key} references \\${${missingVar}} which is not defined`];\n        });\n\n        if (unresolvedEnvEntries.length > 0) {\n          const errorMessage = `Session environment is invalid - environment variables not found in daemon: ${unresolvedEnvEntries.join('; ')}. ` +\n            `Ensure these variables are set in the daemon's environment before starting sessions.`;\n          logger.warn(`[DAEMON RUN] ${errorMessage}`);\n          return {\n            type: 'error',\n            errorMessage\n          };\n        }\n\n        // Check if tmux is available and should be used\n        const tmuxAvailable = await isTmuxAvailable();\n        let useTmux = tmuxAvailable;\n\n        // Get tmux session name from environment variables (now set by profile system)\n        // Empty string means \"use current/most recent session\" (tmux default behavior)\n        let tmuxSessionName: string | undefined = extraEnv.TMUX_SESSION_NAME;\n\n        // If tmux is not available or session name is explicitly undefined, fall back to regular spawning\n        // Note: Empty string is valid (means use current/most recent tmux session)\n        if (!tmuxAvailable || tmuxSessionName === undefined) {\n          useTmux = false;","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/daemon/run.ts#L373-L409","documentation":"When the daemon's spawnSession RPC prepares a session's environment, it expands `${VAR}` references against the daemon's own (ambient) environment. Afterwards it scans every value for leftover `${...}` patterns; if any remain — meaning a referenced variable was never defined in the daemon's environment — the spawn is rejected with a typed error result listing each unresolved reference, and no session process is started.","triggerScenarios":"A session's environmentVariables map contains a value like `\"ANTHROPIC_AUTH_TOKEN\": \"${Z_AI_AUTH_TOKEN}\"` but Z_AI_AUTH_TOKEN is not set in the daemon process's environment (it was set only in the interactive shell that launched previous sessions, or in a shell rc file the detached daemon never sources).","commonSituations":"API keys exported in ~/.zshrc but the daemon runs detached via launchd/systemd or `happy daemon start` from a different shell; user renames an env var in their profile but session templates still reference the old name; tmux vs non-tmux environments diverging; vars set with `sudo` or in a container not visible to the daemon.","solutions":["Export the missing variable in the daemon's environment: stop the daemon (`happy daemon stop`) and start it from a shell where the variable is set, e.g. `Z_AI_AUTH_TOKEN=... happy daemon start`.","Read the error message — it names each `${VAR}` that is undefined — and add those exact variables to the daemon's profile/launch environment.","If the daemon is OS-managed (launchd/systemd), put the vars in the service's environment file (launchctl setenv / EnvironmentFile=) rather than shell rc files.","Alternatively remove or inline the `${VAR}` reference from the session's environmentVariables configuration so no expansion is needed."],"exampleFix":"// before: daemon started without the referenced var\nexport ANTHROPIC_AUTH_TOKEN=\"${Z_AI_AUTH_TOKEN}\"  // Z_AI_AUTH_TOKEN undefined in daemon → error\n// after: start the daemon with the var present\nZ_AI_AUTH_TOKEN=sk-real-key happy daemon stop && Z_AI_AUTH_TOKEN=sk-real-key happy daemon start","handlingStrategy":"validation","validationCode":"// Validate session env vars are resolvable in the daemon's environment before spawning\nfunction hasUnresolvedRefs(env: Record<string, string>): string[] {\n  return Object.entries(env)\n    .filter(([, v]) => typeof v === 'string' && v.includes('${'))\n    .map(([k, v]) => `${k}=${v}`);\n}\nconst unresolved = hasUnresolvedRefs(sessionOptions.environmentVariables ?? {});\nif (unresolved.length > 0) {\n  // Also check each referenced name exists in the daemon process\n  for (const entry of unresolved) {\n    const m = entry.match(/\\$\\{([^}:-]+)/);\n    if (m && !(m[1] in process.env)) {\n      throw new Error(`Define ${m[1]} in the daemon environment before spawning: missing for ${entry}`);\n    }\n  }\n}","typeGuard":"function envRefIsResolvable(value: string, daemonEnv: NodeJS.ProcessEnv): boolean {\n  const m = value.match(/\\$\\{([^}:-]+)(?::-[^}]*)?\\}/);\n  return m === null || daemonEnv[m[1]] !== undefined;\n}","tryCatchPattern":"const result = await daemonRpc('spawn-happy-session', options);\nif (result.type === 'error') {\n  // message lists each `${VAR}` missing from the daemon env\n  console.error(result.errorMessage);\n  // fix: export the named vars where the daemon runs, or inline values, then retry\n}","preventionTips":["Start the daemon from a shell that has all needed API keys exported, or set them in the OS service definition (launchctl setenv / systemd EnvironmentFile).","Remember shell rc files (~/.zshrc, ~/.bashrc) are NOT loaded by the detached daemon.","Keep session environment templates in sync with actual variable names; remove stale ${VAR} references after renaming keys.","Test with `happy daemon status` + a trivial session spawn after changing daemon environment."],"tags":["environment-variables","daemon","session-spawn","configuration"],"backgroundTag":"missing-env-var","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}