larksuite/cli · error
appSecret keychain key %q does not match appId %q (expected
Error message
appSecret keychain key %q does not match appId %q (expected %q); %s
What it means
ValidateSecretKeyMatch guards against a config.json whose appSecret is stored as a keychain SecretRef whose ID does not correspond to the configured appId. The expected key is "appsecret:<appId>" (secretAccountKey); when the stored key differs, the CLI refuses to proceed because the keychain entry most likely holds the secret of a different (or previously renamed) app, and silently using it would authenticate against the wrong tenant/app. The message includes a reconfigure hint telling the user how to fix it.
Source
Thrown at internal/core/secret_resolve.go:65
}
key := secretAccountKey(appId)
if err := kc.Set(keychain.LarkCliService, key, input.Plain); err != nil {
return SecretInput{}, fmt.Errorf("keychain unavailable: %w\nhint: use file: reference in config to bypass keychain", err)
}
return SecretInput{Ref: &SecretRef{Source: "keychain", ID: key}}, nil
}
// ValidateSecretKeyMatch checks that the appSecret keychain key references the
// expected appId. This prevents silent mismatches when config.json is edited by
// hand (e.g. appId changed but appSecret.id still points to the old app).
// Only applicable when appSecret is a keychain SecretRef; other forms are skipped.
func ValidateSecretKeyMatch(appId string, secret SecretInput) error {
if secret.Ref == nil || secret.Ref.Source != "keychain" {
return nil
}
expected := secretAccountKey(appId)
if secret.Ref.ID != expected {
return fmt.Errorf(
"appSecret keychain key %q does not match appId %q (expected %q); %s",
secret.Ref.ID, appId, expected, reconfigureHint(),
)
}
return nil
}
// RemoveSecretStore cleans up keychain entries when an app is removed.
// Errors are intentionally ignored — cleanup is best-effort.
func RemoveSecretStore(input SecretInput, kc keychain.KeychainAccess) {
if input.IsSecretRef() && input.Ref.Source == "keychain" {
_ = kc.Remove(keychain.LarkCliService, input.Ref.ID)
}
}
View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Run the CLI's reconfigure flow (see the hint in the message) so the secret is re-stored under the correct key `appsecret:<appId>`, e.g. re-run `lark-cli config init` with the right app credentials
- If the appSecret is actually stored elsewhere, switch the appSecret entry to a file reference: {"source":"file","id":"/path/to/secret"}, which is not subject to this key match check
- Manually re-key: store the correct secret in the keychain under the expected key and update appSecret.id in config.json to exactly "appsecret:<appId>"
Example fix
// before (config.json)
"appId": "cli_a2f8...",
"appSecret": {"source": "keychain", "id": "appsecret:cli_b1x9..."}
// after
"appId": "cli_a2f8...",
"appSecret": {"source": "keychain", "id": "appsecret:cli_a2f8..."} Defensive patterns
Strategy: validation
Validate before calling
func checkSecretKeyMatch(appId string, secret core.SecretInput) error {
return core.ValidateSecretKeyMatch(appId, secret) // nil when OK
}
// or, before writing config:
expected := "appsecret:" + appId
if secret.Ref != nil && secret.Ref.Source == "keychain" && secret.Ref.ID != expected {
return fmt.Errorf("re-run config init for appId %s (keychain key is %s)", appId, secret.Ref.ID)
} Type guard
func isKeychainRefForApp(s core.SecretInput, appId string) bool {
return s.Ref != nil && s.Ref.Source == "keychain" && s.Ref.ID == "appsecret:"+appId
} Prevention
- Always change apps via `config init` rather than hand-editing config.json
- If hand-editing, update appSecret.id to "appsecret:<newAppId>" together with appId and re-store the secret in the keychain
- Prefer file references for secrets when sharing configs across machines
When it happens
Trigger: A config.json is hand-edited (e.g. appId changed or copied from another profile) while the appSecret entry still reads {"source":"keychain","id":"appsecret:<oldAppId>"}; validation runs during ResolveConfigFromMulti and finds secret.Ref.ID != "appsecret:"+appId.
Common situations: Switching to a different Lark app in config.json by hand; copying another machine's config.json while keychain entries were minted under a different appId; running `config init` for one app then manually editing appId; multiple profiles where the keychain entry was created under the wrong account.
Related errors
- Invalid column: {column!r}
- Invalid column index: {index}
- anchor outside sheet: {position!r}
- Missing row_count/column_count for sheet {sheet_title(sheet)
- malformed config
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/86d8196fe1f278be.
Report an issue: GitHub.