larksuite/cli · error

appSecret is empty string

Error message

appSecret is empty string

What it means

When ResolveSecretInput receives a plain-string SecretInput, resolvePlainOrTemplate checks the string before env-template expansion. This error means the plain string form was selected (Ref == nil) but the string itself is empty — a literal empty appSecret value like "" in openclaw.json. It is distinct from error 161 (no appSecret at all): here the key exists but holds an empty string.

Source

Thrown at internal/binding/secret_resolve.go:40

	}

	if input.IsZero() {
		return "", fmt.Errorf("appSecret is missing or empty")
	}

	// Plain string form (includes env templates)
	if input.IsPlain() {
		return resolvePlainOrTemplate(input.Plain, getenv)
	}

	// 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.

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Replace the empty string with the actual app secret value in openclaw.json.
  2. Better: set "appSecret": "${FEISHU_APP_SECRET}" and export the variable in the environment so the secret is never stored in the file.
  3. In code, pre-validate: if input.IsPlain() && input.Plain == "" { reject before resolve }.
  4. If the channel is not yet configured, either remove appSecret (surfacing error 161) or set "enabled": false.

Example fix

// before
"appSecret": ""
// after
"appSecret": "${FEISHU_APP_SECRET}"
Defensive patterns

Strategy: validation

Validate before calling

if input.IsPlain() && input.Plain == "" {
	return fmt.Errorf("openclaw config: appSecret must be a non-empty string or a secret reference")
}

Type guard

func hasNonEmptyPlainSecret(in binding.SecretInput) bool {
	return in.IsPlain() && in.Plain != ""
}

Try / catch

secret, err := binding.ResolveSecretInput(input, cfg, os.Getenv)
if err != nil {
	if strings.Contains(err.Error(), "appSecret is empty string") {
		return fmt.Errorf("appSecret in openclaw.json is \"\"; set the real secret or an env template: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: openclaw.json contains "appSecret": "" (or accounts.<label>.appSecret set to ""), so SecretInput{Plain: ""} reaches resolvePlainOrTemplate and fails the value == "" check. Can also happen when a template engine or script wrote an empty substituted value.

Common situations: A placeholder was replaced with nothing during provisioning; a user cleared the secret in the editor but left the key; a CI template rendered ${SECRET} from an unset variable, producing "appSecret": "".

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/028286281b966d7f. Report an issue: GitHub.