{"record":{"id":"fea0019b1e5a61e5","repo":"usememos/memos","slug":"config-oauth2config-fieldmapping-identifier-is-req","errorCode":null,"errorMessage":"config.oauth2Config.fieldMapping.identifier is required","messagePattern":"config\\.oauth2Config\\.fieldMapping\\.identifier is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store/deployment_config.go","lineNumber":217,"sourceCode":"\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}\n\t\tif offset := setting.GetGeneralSetting().WeekStartDayOffset; offset < -1 || offset > 6 {\n\t\t\treturn errors.New(\"generalSetting.weekStartDayOffset must be between -1 and 6\")\n\t\t}\n\tcase storepb.InstanceSettingKey_STORAGE:\n\t\tstorage := setting.GetStorageSetting()\n\t\tif storage == nil {\n\t\t\treturn errors.New(\"storageSetting must be populated for key STORAGE\")\n\t\t}","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/deployment_config.go#L199-L235","documentation":"Thrown when config.oauth2Config in a memos-idp-*.json file has no fieldMapping message or its identifier field is blank after trimming. The fieldMapping.identifier tells Memos which userinfo JSON property (e.g. \"login\", \"email\", \"sub\") identifies the user; without it the OAuth2 exchange cannot map a login to a Memos account, so the deployment file is rejected at startup.","triggerScenarios":"config.oauth2Config with \"fieldMapping\": {}, a missing \"fieldMapping\" key, or \"identifier\": \"\" / \"   \".","commonSituations":"Not knowing which userinfo attribute the provider returns (e.g. GitHub uses \"login\", generic OIDC uses \"sub\" or \"email\"); trimming the JSON down for testing and removing fieldMapping.","solutions":["Add \"fieldMapping\": { \"identifier\": \"<userinfo-key>\" } under config.oauth2Config.","For GitHub use \"login\"; for OIDC-compliant providers use \"sub\" or \"email\" depending on the userinfo payload."],"exampleFix":"// before\n\"oauth2Config\": { ..., \"scopes\": [\"read:user\"] }\n\n// after\n\"oauth2Config\": { ..., \"scopes\": [\"read:user\"], \"fieldMapping\": { \"identifier\": \"login\" } }","handlingStrategy":"validation","validationCode":"fm := cfg.GetFieldMapping()\nif fm == nil || strings.TrimSpace(fm.Identifier) == \"\" {\n    return errors.New(\"config.oauth2Config.fieldMapping.identifier is required\")\n}","typeGuard":"func hasIdentifierMapping(cfg *storepb.OAuth2Config) bool {\n    fm := cfg.GetFieldMapping()\n    return fm != nil && strings.TrimSpace(fm.GetIdentifier()) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Inspect the provider's userinfo endpoint response once (curl with a token) and map identifier to a key that is always present, e.g. \"sub\" or \"login\".","Prefer stable identifiers (sub) over emails, which can change."],"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"}