larksuite/cli · error

unknown secret source: %s

Error message

unknown secret source: %s

What it means

ResolveSecretInput switches on the SecretRef source and only understands file and keychain (the ValidSecretSources set). This error is returned when the config's secret reference names a source outside that set, so the secret cannot be resolved.

Source

Thrown at internal/core/secret_resolve.go:36

}

// ResolveSecretInput resolves a SecretInput to a plain string.
// SecretRef objects are resolved by source (file / keychain).
func ResolveSecretInput(s SecretInput, kc keychain.KeychainAccess) (string, error) {
	if s.Ref == nil {
		return s.Plain, nil
	}
	switch s.Ref.Source {
	case "file":
		data, err := vfs.ReadFile(s.Ref.ID)
		if err != nil {
			return "", fmt.Errorf("failed to read secret file %s: %w", s.Ref.ID, err)
		}
		return strings.TrimSpace(string(data)), nil
	case "keychain":
		return kc.Get(keychain.LarkCliService, s.Ref.ID)
	default:
		return "", fmt.Errorf("unknown secret source: %s", s.Ref.Source)
	}
}

// ForStorage determines how to store a secret in config.json.
// - SecretRef → preserved as-is
// - Plain text → stored in keychain, returns keychain SecretRef
// Returns error if keychain is unavailable (no silent plaintext fallback).
func ForStorage(appId string, input SecretInput, kc keychain.KeychainAccess) (SecretInput, error) {
	if !input.IsPlain() {
		return input, nil // SecretRef → keep as-is
	}
	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
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Change source to file and point id at a file containing the secret.
  2. Or change source to keychain with the account key as id (re-run the CLI's config/login flow to store it).
  3. Check spelling and case: only lowercase file and keychain are valid per ValidSecretSources.
  4. If migrating from another secret manager, export the value to a file or the OS keychain first, then reference it.

Example fix

// before (config.json)
"appSecret": {"source": "env", "id": "LARK_APP_SECRET"}
// after
"appSecret": {"source": "keychain", "id": "cli_a1b2c3:appSecret"}
Defensive patterns

Strategy: validation

Validate before calling

if ref := cfg.AppSecret.Ref; ref != nil && !core.ValidSecretSources[ref.Source] {
	return fmt.Errorf("unsupported secret source %q; use file or keychain", ref.Source)
}

Type guard

func knownSecretSource(ref *core.SecretRef) bool {
	return ref != nil && core.ValidSecretSources[ref.Source]
}

Try / catch

secret, err := core.ResolveSecretInput(input, kc)
if err != nil {
	if strings.HasPrefix(err.Error(), "unknown secret source:") {
		// migrate the ref to file/keychain before retrying
	}
	return err
}

Prevention

When it happens

Trigger: Calling ResolveSecretInput (via authLogoutRun or ResolveConfigFromMulti) with a secret stored as {"source":"<other>","id":"..."} where source is not file or keychain - e.g. env, vault, a typo like files, or wrong casing like Keychain.

Common situations: Hand-editing config.json with an unsupported source such as environment or vault; copying configs between CLI versions that supported different source sets; case-sensitivity mistakes.

Related errors


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