larksuite/cli · error
appSecret must be a string or {source, provider?, id} object
Error message
appSecret must be a string or {source, provider?, id} object What it means
This is the fallback rejection in SecretInput.UnmarshalJSON: the JSON value is neither a plain string (plaintext secret) nor a parseable {source, provider?, id} SecretRef object. It is the last guard so any structurally invalid appSecret input fails here.
Source
Thrown at internal/binding/types.go:114
s.Ref = nil
return nil
}
// Try SecretRef object
var ref SecretRef
if err := json.Unmarshal(data, &ref); err == nil {
if !validSources[ref.Source] {
return fmt.Errorf("SecretRef.source must be env|file|exec, got %q", ref.Source)
}
if ref.ID == "" {
return fmt.Errorf("SecretRef.id must be non-empty")
}
s.Ref = &ref
s.Plain = ""
return nil
}
return fmt.Errorf("appSecret must be a string or {source, provider?, id} object")
}
// MarshalJSON serializes SecretInput back to JSON.
func (s SecretInput) MarshalJSON() ([]byte, error) {
if s.Ref != nil {
return json.Marshal(s.Ref)
}
return json.Marshal(s.Plain)
}
// SecretsConfig captures the secrets.providers registry from openclaw.json.
type SecretsConfig struct {
Providers map[string]*ProviderConfig `json:"providers,omitempty"`
Defaults *ProviderDefaults `json:"defaults,omitempty"`
}
// ProviderDefaults holds default provider aliases for each source type.
type ProviderDefaults struct {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Pass a plain string for a literal secret
- Pass a complete {source, provider?, id} object with valid source (env|file|exec) and non-empty id
- Inspect the raw JSON type of the value; quote strings if a YAML/JSON parser coerced them
Example fix
// before
"appSecret": {"source": "env"}
// after
"appSecret": {"source": "env", "id": "FEISHU_APP_SECRET"} Defensive patterns
Strategy: validation
Validate before calling
func validSecretInput(v any) error {
if s, ok := v.(string); ok {
if s == "" { return errors.New("appSecret empty") }
return nil
}
m, ok := v.(map[string]any)
if !ok { return errors.New("appSecret must be string or object") }
src, _ := m["source"].(string)
id, _ := m["id"].(string)
if src != "env" && src != "file" && src != "exec" { return fmt.Errorf("bad source %q", src) }
if id == "" { return errors.New("empty id") }
return nil
} Type guard
func isPlainStringOrRef(v any) bool {
if _, ok := v.(string); ok { return true }
m, ok := v.(map[string]any)
return ok && m["source"] != nil && m["id"] != nil
} Prevention
- Quote secret values in YAML so type coercion cannot turn them into numbers/booleans
- Run a config schema check before deployment
- Use one helper to construct all secret inputs
When it happens
Trigger: appSecret set to a JSON number, boolean, array, object missing required ref fields, or a malformed object that fails SecretRef unmarshal (e.g. {"source":123}).
Common situations: YAML/JSON type coercion surprises (unquoted secret values, keys parsed as numbers); partial ref objects; accidentally pasting a nested structure into a string field.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- SecretRef.source must be env|file|exec, got %q
- SecretRef.id must be non-empty
- malformed config
- file provider path is empty
- singleValue file provider expects ref id %q, got %q
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/3b39c8345491b457.
Report an issue: GitHub.