{"record":{"id":"edb20bdc6498f743","repo":"usememos/memos","slug":"invalidargument-edb20b","errorCode":"InvalidArgument","errorMessage":"AI setting is required","messagePattern":"AI setting is required","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/router/api/v1/instance_service_validation.go","lineNumber":30,"sourceCode":"\n\tv1pb \"github.com/usememos/memos/proto/gen/api/v1\"\n\tstorepb \"github.com/usememos/memos/proto/gen/store\"\n)\n\nfunc validateInstanceSetting(setting *v1pb.InstanceSetting) error {\n\tkey, err := ExtractInstanceSettingKeyFromName(setting.Name)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif key != storepb.InstanceSettingKey_TAGS.String() {\n\t\treturn nil\n\t}\n\treturn validateInstanceTagsSetting(setting.GetTagsSetting())\n}\n\nfunc (s *APIV1Service) prepareInstanceAISettingForUpdate(ctx context.Context, setting *storepb.InstanceAISetting) error {\n\tif setting == nil {\n\t\treturn errors.New(\"AI setting is required\")\n\t}\n\n\texisting, err := s.Store.GetInstanceAISetting(ctx)\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"failed to get existing AI setting\")\n\t}\n\texistingProviders := map[string]*storepb.AIProviderConfig{}\n\tif existing != nil {\n\t\tfor _, provider := range existing.Providers {\n\t\t\tif provider != nil && provider.Id != \"\" {\n\t\t\t\texistingProviders[provider.Id] = provider\n\t\t\t}\n\t\t}\n\t}\n\n\tseenIDs := map[string]bool{}\n\tfor _, provider := range setting.Providers {\n\t\tif provider == nil {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/server/router/api/v1/instance_service_validation.go#L12-L48","documentation":"prepareInstanceAISettingForUpdate rejects an UpdateInstanceSetting call whose setting carries the AI key but a nil tags/AI payload. The instance settings update path first routes by key; for the AI key it requires a non-nil InstanceAISetting protobuf before merging with the existing stored setting. It is a plain validation error surfaced to the caller as InvalidArgument.","triggerScenarios":"UpdateInstanceSetting(instance_setting.name = 'instanceSettings/AI') with ai_setting left unset/nil in the request payload; partially constructed protobuf message where only the name field was set; or a client that assumes the server will treat a missing AI setting as 'no change'.","commonSituations":"Hand-built JSON payloads for the REST/gRPC-Gateway route that omit the aiSetting field; proto schema drift between client SDK and server where the field name changed; toggling AI features off by sending an empty update instead of a disable flag.","solutions":["Send the full InstanceAISetting object (even if only one provider changes); fetch the current value with GetInstanceSetting first and modify it.","If the intent is to clear AI config, send an explicitly empty providers list rather than omitting the message.","Regenerate the TypeScript/Go client from proto after any API change so the field is present in the payload."],"exampleFix":"// before\nawait instanceClient.updateInstanceSetting({\n  setting: { name: 'instanceSettings/AI' } // aiSetting omitted -> nil\n});\n\n// after\nconst current = await instanceClient.getInstanceSetting({ name: 'instanceSettings/AI' });\nawait instanceClient.updateInstanceSetting({\n  setting: { name: 'instanceSettings/AI', aiSetting: current.setting.aiSetting }\n});","handlingStrategy":"validation","validationCode":"function assertAISetting(setting) {\n  if (!setting?.aiSetting) throw new Error('aiSetting message is required for the AI key');\n}","typeGuard":"function isInstanceAISetting(v) {\n  return v != null && typeof v === 'object' && Array.isArray(v.providers);\n}","tryCatchPattern":"try { await updateInstanceSetting(req); } catch (e) { if (e.code === 'invalid_argument' && e.message.includes('AI setting')) { /* re-send with full aiSetting */ } throw e; }","preventionTips":["Read-modify-write instance settings: GetInstanceSetting, mutate, then UpdateInstanceSetting.","Treat omitted optional messages as 'no field sent', never as 'leave unchanged' — this API requires the message.","Regenerate typed clients after proto changes so missing fields fail at compile time."],"tags":["validation","instance-settings","ai-providers","invalid-argument"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}