larksuite/cli · error
SecretRef.source must be env|file|exec, got %q
Error message
SecretRef.source must be env|file|exec, got %q
What it means
SecretInput.UnmarshalJSON rejects a SecretRef whose `source` field is not one of the allowed values env, file, or exec. The check exists because secret refs are dispatched to provider backends keyed by source, and an unknown source would silently fail later at lookup time. Failing fast at JSON unmarshal keeps config errors at load time.
Source
Thrown at internal/binding/types.go:104
// EnvTemplateRe matches OpenClaw env template strings like "${FEISHU_APP_SECRET}".
// Only uppercase letters, digits, and underscores; 1-128 chars; must start with uppercase.
var EnvTemplateRe = regexp.MustCompile(`^\$\{([A-Z][A-Z0-9_]{0,127})\}$`)
// UnmarshalJSON handles both string and object forms of SecretInput.
func (s *SecretInput) UnmarshalJSON(data []byte) error {
// Try string first
var str string
if err := json.Unmarshal(data, &str); err == nil {
s.Plain = str
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)View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Change the source value to one of env, file, or exec
- Declare a custom provider with that source in secrets.providers config before referencing it
- Fix the typo in the source key
Example fix
// before
{"appSecret": {"source": "vault", "id": "bot-1"}}
// after
{"appSecret": {"source": "env", "id": "FEISHU_APP_SECRET"}} Defensive patterns
Strategy: validation
Validate before calling
func validSecretRef(r map[string]any) bool {
src, _ := r["source"].(string)
id, _ := r["id"].(string)
return (src == "env" || src == "file" || src == "exec") && id != ""
} Type guard
func isSecretRef(v any) (source, id string, ok bool) {
m, isMap := v.(map[string]any)
if !isMap {
return "", "", false
}
source, _ = m["source"].(string)
id, _ = m["id"].(string)
return source, id, source == "env" || source == "file" || source == "exec"
} Prevention
- Keep source values to the env|file|exec set; never free-text
- Add schema validation (JSON Schema / config lint) for secret refs in CI
- Generate refs from a helper function instead of hand-writing JSON
When it happens
Trigger: Parsing appSecret config where a SecretRef object has a `source` value outside env|file|exec, e.g. {"source":"vault","id":"x"} or a typo like {"source":"envs","id":"x"}.
Common situations: Hand-editing config files and mistyping source; copying refs from other secret systems (vault, aws-secrets-manager); schema drift after upstream config format changes.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- SecretRef.id must be non-empty
- appSecret must be a string or {source, provider?, id} object
- 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/8e663acd646ec26d.
Report an issue: GitHub.