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

  1. Send the full InstanceAISetting object (even if only one provider changes); fetch the current value with GetInstanceSetting first and modify it.
  2. If the intent is to clear AI config, send an explicitly empty providers list rather than omitting the message.
  3. 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

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.