larksuite/cli · error
appSecret must be a string or {source, id} object
Error message
appSecret must be a string or {source, id} object What it means
The appSecret field in config.json accepts either a plain string or an object of shape {"source": "file"|"keychain", "id": "..."}. SecretInput.UnmarshalJSON returns this error when the JSON value is neither a valid SecretRef (source not in ValidSecretSources or empty id) nor a plain string. It is a config schema validation error raised during unmarshalling.
Source
Thrown at internal/core/secret.go:76
}
// UnmarshalJSON deserializes SecretInput from either a JSON string or a SecretRef object.
func (s *SecretInput) UnmarshalJSON(data []byte) error {
// Try string first
var plain string
if err := json.Unmarshal(data, &plain); err == nil {
s.Plain = plain
s.Ref = nil
return nil
}
// Try SecretRef object
var ref SecretRef
if err := json.Unmarshal(data, &ref); err == nil && isValidSource(ref.Source) && ref.ID != "" {
s.Ref = &ref
s.Plain = ""
return nil
}
return fmt.Errorf("appSecret must be a string or {source, id} object")
}
// ValidSecretSources is the set of recognized SecretRef sources.
var ValidSecretSources = map[string]bool{
"file": true, "keychain": true,
}
func isValidSource(source string) bool {
return ValidSecretSources[source]
}
View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Change appSecret to a plain JSON string: "appSecret": "your-secret".
- Or use a valid reference object: {"source": "file", "id": "/path/to/secret"} or {"source": "keychain", "id": "<account-key>"}.
- Ensure source is exactly file or keychain (case-sensitive) and id is a non-empty string.
- Run the CLI's config init / auth login to let the tool write a correctly shaped secret.
Example fix
// before (config.json)
"appSecret": {"source": "env", "id": "MY_SECRET"}
// after
"appSecret": {"source": "file", "id": "/home/me/.lark/secret.txt"} Defensive patterns
Strategy: validation
Validate before calling
func validateAppSecret(v json.RawMessage) error {
var s string
if json.Unmarshal(v, &s) == nil && s != "" {
return nil
}
var ref struct {
Source string `json:"source"`
ID string `json:"id"`
}
if json.Unmarshal(v, &ref) == nil && core.ValidSecretSources[ref.Source] && ref.ID != "" {
return nil
}
return fmt.Errorf("appSecret must be a string or {source: file|keychain, id: non-empty}")
} Type guard
func isStringOrSecretRef(v json.RawMessage) bool {
var s string
if json.Unmarshal(v, &s) == nil {
return true
}
var ref core.SecretRef
return json.Unmarshal(v, &ref) == nil && core.ValidSecretSources[ref.Source] && ref.ID != ""
} Try / catch
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
if strings.Contains(err.Error(), "appSecret must be") {
// fix the appSecret field shape in config.json
}
return err
} Prevention
- Only use source file or keychain in secret references - the set is case-sensitive.
- Never leave the ref object's id empty when converting a plain secret to a reference.
- Use schema/--help output to confirm accepted appSecret shapes before hand-editing.
- Always quote the secret string; never store it as a number or bare token.
When it happens
Trigger: Unmarshalling config.json where appSecret is a number, boolean, array, or null; or an object whose source is not exactly file or keychain, or whose id is an empty string.
Common situations: Hand-editing config.json and writing appSecret as a number or {"source": "env"}; copying a secret-ref example but leaving "id": ""; a converter producing a non-string type for the secret field.
Related errors
- malformed config
- SecretRef.source must be env|file|exec, got %q
- SecretRef.id must be non-empty
- appSecret must be a string or {source, provider?, id} object
- unknown secret source: %s
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/1a0bfcefc86aedf6.
Report an issue: GitHub.