{"record":{"id":"2ade46242ff20b4d","repo":"usememos/memos","slug":"aisetting-must-be-populated-for-key-ai","errorCode":null,"errorMessage":"aiSetting must be populated for key AI","messagePattern":"aiSetting must be populated for key AI","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store/deployment_config.go","lineNumber":285,"sourceCode":"\t\tif setting.GetMemoRelatedSetting() == nil {\n\t\t\treturn errors.New(\"memoRelatedSetting must be populated for key MEMO_RELATED\")\n\t\t}\n\tcase storepb.InstanceSettingKey_NOTIFICATION:\n\t\tnotification := setting.GetNotificationSetting()\n\t\tif notification == nil {\n\t\t\treturn errors.New(\"notificationSetting must be populated for key NOTIFICATION\")\n\t\t}\n\t\tif email := notification.Email; email != nil && email.Enabled {\n\t\t\tif strings.TrimSpace(email.SmtpHost) == \"\" || email.SmtpPort <= 0 || strings.TrimSpace(email.FromEmail) == \"\" {\n\t\t\t\treturn errors.New(\"enabled notification email requires smtpHost, a positive smtpPort, and fromEmail\")\n\t\t\t}\n\t\t\tif email.UseTls && email.UseSsl {\n\t\t\t\treturn errors.New(\"notification email cannot enable both useTls and useSsl\")\n\t\t\t}\n\t\t}\n\tcase storepb.InstanceSettingKey_AI:\n\t\tif setting.GetAiSetting() == nil {\n\t\t\treturn errors.New(\"aiSetting must be populated for key AI\")\n\t\t}\n\t\tif err := normalizeDeploymentAISetting(setting.GetAiSetting()); err != nil {\n\t\t\treturn err\n\t\t}\n\tcase storepb.InstanceSettingKey_BASIC, storepb.InstanceSettingKey_TAGS:\n\t\treturn errors.Errorf(\"key %s cannot be deployment configured\", setting.Key)\n\tdefault:\n\t\treturn errors.Errorf(\"unsupported instance setting key %s\", setting.Key)\n\t}\n\treturn nil\n}\n\nfunc normalizeDeploymentAISetting(setting *storepb.InstanceAISetting) error {\n\tproviders := map[string]struct{}{}\n\tfor i, provider := range setting.Providers {\n\t\tif provider == nil {\n\t\t\treturn errors.Errorf(\"aiSetting.providers[%d] must not be null\", i)\n\t\t}","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/store/deployment_config.go#L267-L303","documentation":"Thrown when an instance-setting deployment file declares \"key\": \"AI\" but its aiSetting payload message is nil. The aiSetting must be present and is then passed to normalizeDeploymentAISetting for deeper validation (provider references, transcription limits). A key without payload is rejected at startup as an invalid instance setting deployment file.","triggerScenarios":"A memos-instance-setting-ai.json with only { \"key\": \"AI\" } or a mistyped payload key (\"ai_setting\", \"ai\") that protojson ignores.","commonSituations":"Placeholder AI file created before API keys are provisioned; secret-mounting that renders an empty payload object.","solutions":["Add an \"aiSetting\" object, e.g. { \"key\": \"AI\", \"aiSetting\": { \"providers\": [ ... ] } }.","Check the payload key is exactly aiSetting."],"exampleFix":"// before\n{ \"key\": \"AI\" }\n\n// after\n{ \"key\": \"AI\", \"aiSetting\": { \"providers\": [ { \"id\": \"openai\", \"name\": \"OpenAI\", \"type\": \"OPENAI\", \"apiKey\": \"sk-...\" } ] } }","handlingStrategy":"validation","validationCode":"if setting.Key == storepb.InstanceSettingKey_AI && setting.GetAiSetting() == nil {\n    return errors.New(\"AI setting requires an aiSetting payload\")\n}","typeGuard":"func hasAiPayload(s *storepb.InstanceSetting) bool {\n    return s.GetKey() == storepb.InstanceSettingKey_AI && s.GetAiSetting() != nil\n}","tryCatchPattern":null,"preventionTips":["Ship the AI file only when providers with API keys are ready to mount.","Keep the payload key exactly aiSetting."],"tags":["configuration","validation","instance-setting","ai","startup"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}