{"record":{"id":"3b39c8345491b457","repo":"larksuite/cli","slug":"appsecret-must-be-a-string-or-source-provider","errorCode":null,"errorMessage":"appSecret must be a string or {source, provider?, id} object","messagePattern":"appSecret must be a string or (.+?) object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/binding/types.go","lineNumber":114,"sourceCode":"\t\ts.Ref = nil\n\t\treturn nil\n\t}\n\n\t// Try SecretRef object\n\tvar ref SecretRef\n\tif err := json.Unmarshal(data, &ref); err == nil {\n\t\tif !validSources[ref.Source] {\n\t\t\treturn fmt.Errorf(\"SecretRef.source must be env|file|exec, got %q\", ref.Source)\n\t\t}\n\t\tif ref.ID == \"\" {\n\t\t\treturn fmt.Errorf(\"SecretRef.id must be non-empty\")\n\t\t}\n\t\ts.Ref = &ref\n\t\ts.Plain = \"\"\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"appSecret must be a string or {source, provider?, id} object\")\n}\n\n// MarshalJSON serializes SecretInput back to JSON.\nfunc (s SecretInput) MarshalJSON() ([]byte, error) {\n\tif s.Ref != nil {\n\t\treturn json.Marshal(s.Ref)\n\t}\n\treturn json.Marshal(s.Plain)\n}\n\n// SecretsConfig captures the secrets.providers registry from openclaw.json.\ntype SecretsConfig struct {\n\tProviders map[string]*ProviderConfig `json:\"providers,omitempty\"`\n\tDefaults  *ProviderDefaults          `json:\"defaults,omitempty\"`\n}\n\n// ProviderDefaults holds default provider aliases for each source type.\ntype ProviderDefaults struct {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/binding/types.go#L96-L132","documentation":"This is the fallback rejection in SecretInput.UnmarshalJSON: the JSON value is neither a plain string (plaintext secret) nor a parseable {source, provider?, id} SecretRef object. It is the last guard so any structurally invalid appSecret input fails here.","triggerScenarios":"appSecret set to a JSON number, boolean, array, object missing required ref fields, or a malformed object that fails SecretRef unmarshal (e.g. {\"source\":123}).","commonSituations":"YAML/JSON type coercion surprises (unquoted secret values, keys parsed as numbers); partial ref objects; accidentally pasting a nested structure into a string field.","solutions":["Pass a plain string for a literal secret","Pass a complete {source, provider?, id} object with valid source (env|file|exec) and non-empty id","Inspect the raw JSON type of the value; quote strings if a YAML/JSON parser coerced them"],"exampleFix":"// before\n\"appSecret\": {\"source\": \"env\"}\n// after\n\"appSecret\": {\"source\": \"env\", \"id\": \"FEISHU_APP_SECRET\"}","handlingStrategy":"validation","validationCode":"func validSecretInput(v any) error {\n    if s, ok := v.(string); ok {\n        if s == \"\" { return errors.New(\"appSecret empty\") }\n        return nil\n    }\n    m, ok := v.(map[string]any)\n    if !ok { return errors.New(\"appSecret must be string or object\") }\n    src, _ := m[\"source\"].(string)\n    id, _ := m[\"id\"].(string)\n    if src != \"env\" && src != \"file\" && src != \"exec\" { return fmt.Errorf(\"bad source %q\", src) }\n    if id == \"\" { return errors.New(\"empty id\") }\n    return nil\n}","typeGuard":"func isPlainStringOrRef(v any) bool {\n    if _, ok := v.(string); ok { return true }\n    m, ok := v.(map[string]any)\n    return ok && m[\"source\"] != nil && m[\"id\"] != nil\n}","tryCatchPattern":null,"preventionTips":["Quote secret values in YAML so type coercion cannot turn them into numbers/booleans","Run a config schema check before deployment","Use one helper to construct all secret inputs"],"tags":["config","json","validation","secrets"],"backgroundTag":"schema-validation-failed","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"}