{"record":{"id":"cce0a72d6dfd6119","repo":"usememos/memos","slug":"config-oauth2config-is-required","errorCode":null,"errorMessage":"config.oauth2Config is required","messagePattern":"config\\.oauth2Config is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store/deployment_config.go","lineNumber":178,"sourceCode":"\t\treturn errors.New(\"id must be omitted\")\n\t}\n\tif !base.UIDMatcher.MatchString(provider.Uid) {\n\t\treturn errors.New(\"uid is invalid\")\n\t}\n\tif strings.TrimSpace(provider.Name) == \"\" {\n\t\treturn errors.New(\"name is required\")\n\t}\n\tif provider.Type != storepb.IdentityProvider_OAUTH2 {\n\t\treturn errors.New(\"type must be OAUTH2\")\n\t}\n\tif provider.IdentifierFilter != \"\" {\n\t\tif _, err := regexp.Compile(provider.IdentifierFilter); err != nil {\n\t\t\treturn errors.Wrap(err, \"identifierFilter must be a valid regular expression\")\n\t\t}\n\t}\n\tconfig := provider.Config.GetOauth2Config()\n\tif config == nil {\n\t\treturn errors.New(\"config.oauth2Config is required\")\n\t}\n\trequired := []struct {\n\t\tname  string\n\t\tvalue string\n\t}{\n\t\t{name: \"clientId\", value: config.ClientId},\n\t\t{name: \"clientSecret\", value: config.ClientSecret},\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\tfor _, field := range required {\n\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","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/deployment_config.go#L160-L196","documentation":"Thrown when an identity-provider deployment file has no \"config\" message or its config does not carry the oauth2Config oneof member (config.GetOauth2Config() returns nil). Since the file loader only supports type OAUTH2, the oauth2Config block is mandatory: it holds clientId, clientSecret, authUrl, tokenUrl, userInfoUrl, scopes and fieldMapping. Startup aborts with 'invalid identity provider deployment file'.","triggerScenarios":"A memos-idp-*.json with \"config\": {} or no \"config\" key at all, or with a typo'd key like \"oauthConfig\" or \"oauth2config\" that protojson silently ignores (unknown fields produce a separate error, but wrong-cased oneof members may leave the message empty).","commonSituations":"Hand-writing the JSON and nesting the OAuth fields directly under \"config\" instead of under \"config.oauth2Config\"; using snake_case \"oauth2_config\" inconsistently; copying from docs that show only the inner fields.","solutions":["Wrap the OAuth fields: add \"config\": { \"oauth2Config\": { ... } } to the memos-idp-*.json file.","Check the key spelling and casing exactly: config.oauth2Config (protojson lowerCamelCase).","Validate the file renders the full structure if it is templated."],"exampleFix":"// before\n{ \"uid\": \"github\", \"name\": \"GitHub\", \"type\": \"OAUTH2\",\n  \"config\": { \"clientId\": \"...\", \"authUrl\": \"...\" } }\n\n// after\n{ \"uid\": \"github\", \"name\": \"GitHub\", \"type\": \"OAUTH2\",\n  \"config\": { \"oauth2Config\": {\n    \"clientId\": \"...\", \"clientSecret\": \"...\",\n    \"authUrl\": \"...\", \"tokenUrl\": \"...\", \"userInfoUrl\": \"...\",\n    \"scopes\": [\"read:user\"],\n    \"fieldMapping\": { \"identifier\": \"login\" }\n  } } }","handlingStrategy":"validation","validationCode":"if provider.Config == nil || provider.Config.GetOauth2Config() == nil {\n    return errors.New(\"idp config.oauth2Config is required\")\n}","typeGuard":"func hasOauth2Config(c *storepb.IdentityProviderConfig) bool {\n    return c != nil && c.GetOauth2Config() != nil\n}","tryCatchPattern":null,"preventionTips":["Keep a known-good memos-idp-*.json example in the repo and start from it.","Remember protojson keys are lowerCamelCase: config.oauth2Config.clientId."],"tags":["configuration","validation","identity-provider","oauth2","startup"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}