{"record":{"id":"b2439e3857e10276","repo":"larksuite/cli","slug":"unknown-secret-source-s","errorCode":null,"errorMessage":"unknown secret source: %s","messagePattern":"unknown secret source: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/core/secret_resolve.go","lineNumber":36,"sourceCode":"}\n\n// ResolveSecretInput resolves a SecretInput to a plain string.\n// SecretRef objects are resolved by source (file / keychain).\nfunc ResolveSecretInput(s SecretInput, kc keychain.KeychainAccess) (string, error) {\n\tif s.Ref == nil {\n\t\treturn s.Plain, nil\n\t}\n\tswitch s.Ref.Source {\n\tcase \"file\":\n\t\tdata, err := vfs.ReadFile(s.Ref.ID)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to read secret file %s: %w\", s.Ref.ID, err)\n\t\t}\n\t\treturn strings.TrimSpace(string(data)), nil\n\tcase \"keychain\":\n\t\treturn kc.Get(keychain.LarkCliService, s.Ref.ID)\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unknown secret source: %s\", s.Ref.Source)\n\t}\n}\n\n// ForStorage determines how to store a secret in config.json.\n// - SecretRef → preserved as-is\n// - Plain text → stored in keychain, returns keychain SecretRef\n// Returns error if keychain is unavailable (no silent plaintext fallback).\nfunc ForStorage(appId string, input SecretInput, kc keychain.KeychainAccess) (SecretInput, error) {\n\tif !input.IsPlain() {\n\t\treturn input, nil // SecretRef → keep as-is\n\t}\n\tkey := secretAccountKey(appId)\n\tif err := kc.Set(keychain.LarkCliService, key, input.Plain); err != nil {\n\t\treturn SecretInput{}, fmt.Errorf(\"keychain unavailable: %w\\nhint: use file: reference in config to bypass keychain\", err)\n\t}\n\treturn SecretInput{Ref: &SecretRef{Source: \"keychain\", ID: key}}, nil\n}\n","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/core/secret_resolve.go#L18-L54","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change source to file and point id at a file containing the secret.","Or change source to keychain with the account key as id (re-run the CLI's config/login flow to store it).","Check spelling and case: only lowercase file and keychain are valid per ValidSecretSources.","If migrating from another secret manager, export the value to a file or the OS keychain first, then reference it."],"exampleFix":"// before (config.json)\n\"appSecret\": {\"source\": \"env\", \"id\": \"LARK_APP_SECRET\"}\n// after\n\"appSecret\": {\"source\": \"keychain\", \"id\": \"cli_a1b2c3:appSecret\"}","handlingStrategy":"validation","validationCode":"if ref := cfg.AppSecret.Ref; ref != nil && !core.ValidSecretSources[ref.Source] {\n\treturn fmt.Errorf(\"unsupported secret source %q; use file or keychain\", ref.Source)\n}","typeGuard":"func knownSecretSource(ref *core.SecretRef) bool {\n\treturn ref != nil && core.ValidSecretSources[ref.Source]\n}","tryCatchPattern":"secret, err := core.ResolveSecretInput(input, kc)\nif err != nil {\n\tif strings.HasPrefix(err.Error(), \"unknown secret source:\") {\n\t\t// migrate the ref to file/keychain before retrying\n\t}\n\treturn err\n}","preventionTips":["Only emit {source: file} or {source: keychain} refs - no custom sources.","Watch case sensitivity: Keychain and File are invalid.","When migrating configs between environments, convert unsupported sources to file refs first.","Diff hand-edited config.json against a known-good example to catch source typos."],"tags":["config","secret","validation","unsupported-source"],"backgroundTag":"invalid-secret-source","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}