larksuite/cli · error

appSecret is missing or empty

Error message

appSecret is missing or empty

What it means

ResolveSecretInput is the dispatcher that turns a SecretInput (plain string, "${VAR}" template, or SecretRef object) into a plaintext app secret. This error means the SecretInput is zero: the parsed appSecret field carried neither a Plain string nor a Ref object — i.e. the appSecret key was absent, JSON null, or an empty-string/object combination that left both fields unset.

Source

Thrown at internal/binding/secret_resolve.go:25

	"fmt"
	"os"
)

// ResolveSecretInput resolves a SecretInput to a plain-text secret string.
// This is the main dispatcher that handles all SecretInput forms:
//   - Plain string passthrough
//   - "${VAR_NAME}" env template expansion
//   - SecretRef object routing to env/file/exec sub-resolvers
//
// The getenv parameter allows injection for testing (typically os.Getenv).
// This function is only called during config bind (cold path).
func ResolveSecretInput(input SecretInput, cfg *SecretsConfig, getenv func(string) string) (string, error) {
	if getenv == nil {
		getenv = os.Getenv
	}

	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}"

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Add a non-empty appSecret to the channel in openclaw.json: a literal string, an env template "${FEISHU_APP_SECRET}", or a {"source":"env","id":"..."} SecretRef.
  2. If the secret must stay out of the file, set appSecret to "${FEISHU_APP_SECRET}" and export that variable before binding.
  3. In code, guard before calling: if input.IsZero() { return error / skip channel }.
  4. If appSecret: null was intentional to disable the channel, set "enabled": false instead.

Example fix

// before
{ "channels": { "feishu": { "appId": "cli_a1b2" } } }
// after
{ "channels": { "feishu": { "appId": "cli_a1b2", "appSecret": "${FEISHU_APP_SECRET}" } } }
Defensive patterns

Strategy: validation

Validate before calling

if input.IsZero() {
	return fmt.Errorf("openclaw config: channels.feishu.appSecret is required before binding")
}

Type guard

func hasAppSecret(in binding.SecretInput) bool {
	return !in.IsZero()
}

Try / catch

secret, err := binding.ResolveSecretInput(input, cfg, os.Getenv)
if err != nil {
	if strings.Contains(err.Error(), "appSecret is missing or empty") {
		return fmt.Errorf("add appSecret to channels.feishu in openclaw.json: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling ResolveSecretInput with a SecretInput where IsZero() is true: appSecret omitted from channels.feishu (or an accounts.* entry) in openclaw.json, or explicitly set to null, so UnmarshalJSON never populated Plain or Ref.

Common situations: A fresh openclaw.json template has channels.feishu.appId filled but appSecret still missing; a JSON merge or sed script dropped the appSecret key; a user set appSecret: null expecting it to disable the channel.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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