{"record":{"id":"4bc5c0b0e724fcec","repo":"usememos/memos","slug":"config-oauth2config-scopes-is-required","errorCode":null,"errorMessage":"config.oauth2Config.scopes is required","messagePattern":"config\\.oauth2Config\\.scopes is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store/deployment_config.go","lineNumber":209,"sourceCode":"\t\tif strings.TrimSpace(field.value) == \"\" {\n\t\t\treturn errors.Errorf(\"config.oauth2Config.%s is required\", field.name)\n\t\t}\n\t}\n\tfor _, field := range []struct {\n\t\tname  string\n\t\tvalue string\n\t}{\n\t\t{name: \"authUrl\", value: config.AuthUrl},\n\t\t{name: \"tokenUrl\", value: config.TokenUrl},\n\t\t{name: \"userInfoUrl\", value: config.UserInfoUrl},\n\t} {\n\t\tparsed, err := url.ParseRequestURI(field.value)\n\t\tif err != nil || (parsed.Scheme != \"http\" && parsed.Scheme != \"https\") || parsed.Host == \"\" {\n\t\t\treturn errors.Errorf(\"config.oauth2Config.%s must be an absolute HTTP(S) URL\", field.name)\n\t\t}\n\t}\n\tif len(config.Scopes) == 0 {\n\t\treturn errors.New(\"config.oauth2Config.scopes is required\")\n\t}\n\tfor i, scope := range config.Scopes {\n\t\tif strings.TrimSpace(scope) == \"\" {\n\t\t\treturn errors.Errorf(\"config.oauth2Config.scopes[%d] must not be empty\", i)\n\t\t}\n\t}\n\tif config.FieldMapping == nil || strings.TrimSpace(config.FieldMapping.Identifier) == \"\" {\n\t\treturn errors.New(\"config.oauth2Config.fieldMapping.identifier is required\")\n\t}\n\treturn nil\n}\n\nfunc validateAndNormalizeDeploymentInstanceSetting(setting *storepb.InstanceSetting) error {\n\tswitch setting.Key {\n\tcase storepb.InstanceSettingKey_GENERAL:\n\t\tif setting.GetGeneralSetting() == nil {\n\t\t\treturn errors.New(\"generalSetting must be populated for key GENERAL\")\n\t\t}","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/deployment_config.go#L191-L227","documentation":"Thrown when the oauth2Config block of an identity-provider deployment file has an empty or missing \"scopes\" array. OAuth2 authorization requests need at least one scope; the deployment validator treats an empty list as a misconfiguration and fails startup rather than applying a provider that could never complete an auth flow.","triggerScenarios":"config.oauth2Config in a memos-idp-*.json with no \"scopes\" key, \"scopes\": [], or scopes containing only whitespace strings (that variant produces the scopes[i] error instead).","commonSituations":"Providers like generic OIDC where the author omitted scopes; migrating a DB-stored provider whose scopes list was empty; assuming the server injects a default scope.","solutions":["Add the provider's scopes, e.g. \"scopes\": [\"openid\", \"profile\", \"email\"] under config.oauth2Config.","Check the upstream provider's docs for the minimal scope set that exposes the identifier field used in fieldMapping."],"exampleFix":"// before\n\"oauth2Config\": { \"clientId\": \"...\", \"authUrl\": \"...\", \"tokenUrl\": \"...\", \"userInfoUrl\": \"...\", \"fieldMapping\": { ... } }\n\n// after\n\"oauth2Config\": { \"clientId\": \"...\", \"authUrl\": \"...\", \"tokenUrl\": \"...\", \"userInfoUrl\": \"...\", \"scopes\": [\"openid\", \"profile\", \"email\"], \"fieldMapping\": { ... } }","handlingStrategy":"validation","validationCode":"cfg := provider.Config.GetOauth2Config()\nif len(cfg.Scopes) == 0 {\n    return errors.New(\"config.oauth2Config.scopes must not be empty\")\n}","typeGuard":"func hasScopes(cfg *storepb.OAuth2Config) bool {\n    ok := len(cfg.GetScopes()) > 0\n    for _, s := range cfg.GetScopes() {\n        ok = ok && strings.TrimSpace(s) != \"\"\n    }\n    return ok\n}","tryCatchPattern":null,"preventionTips":["Look up the provider's minimal scope set in its OAuth docs before writing the file.","Include scopes in any example/template so they are not dropped when copying."],"tags":["configuration","validation","identity-provider","oauth2","startup"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}