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

  1. 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
  2. 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
  3. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/86d8196fe1f278be. Report an issue: GitHub.