larksuite/cli · error
env variable %q referenced in openclaw.json is not set or em
Error message
env variable %q referenced in openclaw.json is not set or empty
What it means
resolvePlainOrTemplate expands "${VAR_NAME}" env templates in plain appSecret strings using the injected getenv (default os.Getenv). This error means the template matched EnvTemplateRe (uppercase name, 1-128 chars) but the referenced environment variable is unset or empty, so the secret cannot be resolved to a usable value. The variable name is quoted in the message.
Source
Thrown at internal/binding/secret_resolve.go:49
}
// SecretRef object form
return resolveSecretRef(input.Ref, cfg, getenv)
}
// resolvePlainOrTemplate handles plain strings and "${VAR}" templates.
func resolvePlainOrTemplate(value string, getenv func(string) string) (string, error) {
if value == "" {
return "", fmt.Errorf("appSecret is empty string")
}
// Check for env template pattern: "${VAR_NAME}"
matches := EnvTemplateRe.FindStringSubmatch(value)
if matches != nil {
varName := matches[1]
envValue := getenv(varName)
if envValue == "" {
return "", fmt.Errorf("env variable %q referenced in openclaw.json is not set or empty", varName)
}
return envValue, nil
}
// Plain string: use as-is
return value, nil
}
// resolveSecretRef dispatches a SecretRef to the appropriate sub-resolver.
func resolveSecretRef(ref *SecretRef, cfg *SecretsConfig, getenv func(string) string) (string, error) {
// Lookup provider configuration
providerConfig, err := LookupProvider(ref, cfg)
if err != nil {
return "", err
}
// Resolve the effective provider name once so downstream resolvers
// (notably the exec JSON payload) see the config-defaulted value insteadView on GitHub (pinned to 7fd6ef3c07)
Solutions
- Export the named variable with a non-empty value in the same environment that runs the bind: export FEISHU_APP_SECRET=... then re-run.
- Verify the exact name/typo: echo "${FEISHU_APP_SECRET}" in the failing shell; the message shows the name being looked up.
- If using a .env file, source it (set -a; . ./.env; set +a) or use the launcher (direnv, systemd EnvironmentFile) that forwards it to the process.
- Fix empty-string exports: a variable set to "" still fails; ensure a real secret value.
- In code, pre-check with the same getenv: os.LookupEnv(name) and reject !ok || value == "" before binding.
Example fix
// before (shell) lark bind # FEISHU_APP_SECRET not exported // after export FEISHU_APP_SECRET="xxxx" lark bind
Defensive patterns
Strategy: validation
Validate before calling
func checkEnvTemplates(data []byte, lookup func(string) (string, bool)) error {
re := regexp.MustCompile(`^\$\{([A-Z][A-Z0-9_]{0,127})\}$`)
var cfg struct {
Channels struct {
Feishu struct {
AppSecret string `json:"appSecret"`
} `json:"feishu"`
} `json:"channels"`
}
if err := json.Unmarshal(data, &cfg); err != nil { return err }
v := cfg.Channels.Feishu.AppSecret
if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") {
name := re.FindStringSubmatch(v)
if name == nil { return nil }
if val, ok := lookup(name[1]); !ok || val == "" {
return fmt.Errorf("env variable %s must be exported before binding", name[1])
}
}
return nil
} Type guard
func envVarSet(name string) bool {
v, ok := os.LookupEnv(name)
return ok && v != ""
} Try / catch
secret, err := binding.ResolveSecretInput(input, cfg, os.Getenv)
if err != nil {
var envErr *os.PathError // not expected; demonstrate typed inspection
_ = envErr
if m := regexp.MustCompile(`env variable "([^"]+)"`).FindStringSubmatch(err.Error()); m != nil {
return fmt.Errorf("export %s before running bind (see .env / shell profile): %w", m[1], err)
}
return err
} Prevention
- Source your .env (set -a; . ./.env; set +a) or use direnv/systemd EnvironmentFile before running bind.
- Keep variable names in openclaw.json and your shell profile identical; grep both for the exact name.
- Remember the template must match ^\$\{[A-Z][A-Z0-9_]*\}$ — uppercase, no spaces, whole string.
- Check unset-but-exported-to-empty cases with os.LookupEnv in pre-flight scripts.
- Document required env vars in a README or .env.example checked into the repo.
When it happens
Trigger: openclaw.json has "appSecret": "${SOME_VAR}" and getenv("SOME_VAR") returns "" at bind time: the variable was never exported, was exported only in another shell/session, was misspelled, or is set to empty string. Note the regex anchors the whole string, so "prefix${VAR}" does not match the template branch at all.
Common situations: Secret exported in ~/.zshrc but the CLI runs under a systemd unit or CI job without it; dotenv file not sourced before running bind; variable name typo between openclaw.json and export statement; secrets manager wrote an empty value.
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
- appSecret is missing or empty
- appSecret is empty string
- unsupported secret source %q
- environment variable %q is not allowlisted in provider
- environment variable %q is missing or empty
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/29f3cf77d9977dce.
Report an issue: GitHub.