slopus/happy · warning

[EXPAND ENV] WARNING: ${varName} is set but EMPTY in daemon

Error message

[EXPAND ENV] WARNING: ${varName} is set but EMPTY in daemon environment

What it means

expandEnvironmentVariables resolves ${VAR} references in profile environment using the daemon's environment. When a referenced variable IS present in the daemon env but resolves to the empty string, this warning is logged — usually a misconfiguration rather than a genuinely intended empty value.

Source

Thrown at packages/happy-cli/src/utils/expandEnvVars.ts:68

                // Simple ${VAR} reference
                varName = expr;
            }

            const resolvedValue = sourceEnv[varName];
            if (resolvedValue !== undefined) {
                // Variable found in source environment - use its value
                // Log for debugging (mask secret-looking values)
                const isSensitive = varName.toLowerCase().includes('token') ||
                                   varName.toLowerCase().includes('key') ||
                                   varName.toLowerCase().includes('secret');
                const displayValue = isSensitive
                    ? (resolvedValue ? `<${resolvedValue.length} chars>` : '<empty>')
                    : resolvedValue;
                logger.debug(`[EXPAND ENV] Expanded ${varName} from daemon env: ${displayValue}`);

                // Warn if empty string (common mistake)
                if (resolvedValue === '') {
                    logger.warn(`[EXPAND ENV] WARNING: ${varName} is set but EMPTY in daemon environment`);
                }

                return resolvedValue;
            } else if (defaultValue !== undefined) {
                // Variable not found but default value provided - use default
                logger.debug(`[EXPAND ENV] Using default value for ${varName}: ${defaultValue}`);
                return defaultValue;
            } else {
                // Variable not found and no default - keep placeholder and warn
                undefinedVars.push(varName);
                return match;
            }
        });

        expanded[key] = expandedValue;
    }

    // Log warning if any variables couldn't be resolved

View on GitHub (pinned to b824cd0a46)

Solutions

  1. Set the variable to a real value in the daemon's environment (systemd Environment=, docker -e, or the daemon's env file) and restart the daemon.
  2. If empty is legitimate, silence by using a default in the profile reference, e.g. ${VAR:-default}.
  3. Check the daemon env with `sudo systemctl show <daemon> -p Environment` or by dumping /proc/<pid>/environ.
  4. Remove the export VAR= line from shell profiles if it was accidental.

Example fix

// before (systemd unit)
Environment=GEMINI_API_KEY=
// after
Environment=GEMINI_API_KEY=AIza...
Defensive patterns

Strategy: validation

Validate before calling

for (const v of ['MY_TOKEN', 'GEMINI_API_KEY']) {
  if (process.env[v] === undefined) console.warn(`${v}: NOT SET in daemon env`);
  else if (process.env[v] === '') console.warn(`${v}: set but EMPTY — fix daemon env file`);
}

Prevention

When it happens

Trigger: A profile env value like "TOKEN=${MY_TOKEN}" where MY_TOKEN exists in the daemon's process.env but equals "" (exported empty in systemd unit, docker ENV FOO=, or a shell profile exporting VAR=).

Common situations: Daemon (systemd/launchd/docker) environment defining the var as empty; user edited a dotenv/unit file and left the value blank; secrets manager injected an empty placeholder.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of slopus/happy@b824cd0a46 (2026-08-31). Data as JSON: /api/errors/d6b4050dcb092f4d. Report an issue: GitHub.