{"record":{"id":"3c47bd6f94a92ec0","repo":"larksuite/cli","slug":"appsecret-is-missing-or-empty","errorCode":null,"errorMessage":"appSecret is missing or empty","messagePattern":"appSecret is missing or empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/binding/secret_resolve.go","lineNumber":25,"sourceCode":"\t\"fmt\"\n\t\"os\"\n)\n\n// ResolveSecretInput resolves a SecretInput to a plain-text secret string.\n// This is the main dispatcher that handles all SecretInput forms:\n//   - Plain string passthrough\n//   - \"${VAR_NAME}\" env template expansion\n//   - SecretRef object routing to env/file/exec sub-resolvers\n//\n// The getenv parameter allows injection for testing (typically os.Getenv).\n// This function is only called during config bind (cold path).\nfunc ResolveSecretInput(input SecretInput, cfg *SecretsConfig, getenv func(string) string) (string, error) {\n\tif getenv == nil {\n\t\tgetenv = os.Getenv\n\t}\n\n\tif input.IsZero() {\n\t\treturn \"\", fmt.Errorf(\"appSecret is missing or empty\")\n\t}\n\n\t// Plain string form (includes env templates)\n\tif input.IsPlain() {\n\t\treturn resolvePlainOrTemplate(input.Plain, getenv)\n\t}\n\n\t// SecretRef object form\n\treturn resolveSecretRef(input.Ref, cfg, getenv)\n}\n\n// resolvePlainOrTemplate handles plain strings and \"${VAR}\" templates.\nfunc resolvePlainOrTemplate(value string, getenv func(string) string) (string, error) {\n\tif value == \"\" {\n\t\treturn \"\", fmt.Errorf(\"appSecret is empty string\")\n\t}\n\n\t// Check for env template pattern: \"${VAR_NAME}\"","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/binding/secret_resolve.go#L7-L43","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","If the secret must stay out of the file, set appSecret to \"${FEISHU_APP_SECRET}\" and export that variable before binding.","In code, guard before calling: if input.IsZero() { return error / skip channel }.","If appSecret: null was intentional to disable the channel, set \"enabled\": false instead."],"exampleFix":"// before\n{ \"channels\": { \"feishu\": { \"appId\": \"cli_a1b2\" } } }\n// after\n{ \"channels\": { \"feishu\": { \"appId\": \"cli_a1b2\", \"appSecret\": \"${FEISHU_APP_SECRET}\" } } }","handlingStrategy":"validation","validationCode":"if input.IsZero() {\n\treturn fmt.Errorf(\"openclaw config: channels.feishu.appSecret is required before binding\")\n}","typeGuard":"func hasAppSecret(in binding.SecretInput) bool {\n\treturn !in.IsZero()\n}","tryCatchPattern":"secret, err := binding.ResolveSecretInput(input, cfg, os.Getenv)\nif err != nil {\n\tif strings.Contains(err.Error(), \"appSecret is missing or empty\") {\n\t\treturn fmt.Errorf(\"add appSecret to channels.feishu in openclaw.json: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Use a provisioning template for openclaw.json that fails loudly when appSecret is missing.","Prefer env templates (\"${FEISHU_APP_SECRET}\") so the key is always present even though the value is external.","Use \"enabled\": false, not null/absent appSecret, to disable a channel.","Lint the config before bind: reject accounts entries lacking appSecret."],"tags":["config","secrets","missing-field"],"backgroundTag":"missing-required-config-field","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}