usememos/memos · error
InvalidArgument
InvalidArgument
Error message
AI setting is required
What it means
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.
Source
Thrown at server/router/api/v1/instance_service_validation.go:30
v1pb "github.com/usememos/memos/proto/gen/api/v1"
storepb "github.com/usememos/memos/proto/gen/store"
)
func validateInstanceSetting(setting *v1pb.InstanceSetting) error {
key, err := ExtractInstanceSettingKeyFromName(setting.Name)
if err != nil {
return err
}
if key != storepb.InstanceSettingKey_TAGS.String() {
return nil
}
return validateInstanceTagsSetting(setting.GetTagsSetting())
}
func (s *APIV1Service) prepareInstanceAISettingForUpdate(ctx context.Context, setting *storepb.InstanceAISetting) error {
if setting == nil {
return errors.New("AI setting is required")
}
existing, err := s.Store.GetInstanceAISetting(ctx)
if err != nil {
return errors.Wrap(err, "failed to get existing AI setting")
}
existingProviders := map[string]*storepb.AIProviderConfig{}
if existing != nil {
for _, provider := range existing.Providers {
if provider != nil && provider.Id != "" {
existingProviders[provider.Id] = provider
}
}
}
seenIDs := map[string]bool{}
for _, provider := range setting.Providers {
if provider == nil {View on GitHub (pinned to 14d757ce1f)
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.
Example fix
// before
await instanceClient.updateInstanceSetting({
setting: { name: 'instanceSettings/AI' } // aiSetting omitted -> nil
});
// after
const current = await instanceClient.getInstanceSetting({ name: 'instanceSettings/AI' });
await instanceClient.updateInstanceSetting({
setting: { name: 'instanceSettings/AI', aiSetting: current.setting.aiSetting }
}); Defensive patterns
Strategy: validation
Validate before calling
function assertAISetting(setting) {
if (!setting?.aiSetting) throw new Error('aiSetting message is required for the AI key');
} Type guard
function isInstanceAISetting(v) {
return v != null && typeof v === 'object' && Array.isArray(v.providers);
} Try / catch
try { await updateInstanceSetting(req); } catch (e) { if (e.code === 'invalid_argument' && e.message.includes('AI setting')) { /* re-send with full aiSetting */ } throw e; } Prevention
- 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.
When it happens
Trigger: 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'.
Common situations: 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.
Related errors
AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15).
Data as JSON: /api/errors/edb20bdc6498f743.
Report an issue: GitHub.