usememos/memos · error

storageSetting default storage S3 config is required

Error message

storageSetting default storage S3 config is required

What it means

Thrown when the resolved default storage in a STORAGE instance-setting file has type S3 but its embedded s3Config message is nil. GetDefaultStorage(storage) picks the active/default storage (either the legacy top-level s3Config or a storages[] entry); when that storage says S3 but carries no config object, validation fails rather than letting uploads hit a nil config at runtime.

Source

Thrown at store/deployment_config.go:249

	case storepb.InstanceSettingKey_STORAGE:
		storage := setting.GetStorageSetting()
		if storage == nil {
			return errors.New("storageSetting must be populated for key STORAGE")
		}
		// Normalization would silently self-heal this misconfiguration to LOCAL;
		// a deployment file declaring S3 without a config should fail loudly.
		if storage.StorageType == storepb.InstanceStorageSetting_S3 && storage.S3Config == nil && len(storage.Storages) == 0 {
			return errors.New("storageSetting.s3Config is required for S3")
		}
		NormalizeInstanceStorageSetting(storage)
		if storage.UploadSizeLimitMb < 0 {
			return errors.New("storageSetting.uploadSizeLimitMb must not be negative")
		}
		defaultStorage := GetDefaultStorage(storage)
		if defaultStorage != nil && defaultStorage.Type == storepb.StorageType_STORAGE_TYPE_S3 {
			s3Config := defaultStorage.GetS3Config()
			if s3Config == nil {
				return errors.New("storageSetting default storage S3 config is required")
			}
			for _, field := range []struct {
				name  string
				value string
			}{
				{name: "accessKeyId", value: s3Config.AccessKeyId},
				{name: "accessKeySecret", value: s3Config.AccessKeySecret},
				{name: "endpoint", value: s3Config.Endpoint},
				{name: "region", value: s3Config.Region},
				{name: "bucket", value: s3Config.Bucket},
			} {
				if strings.TrimSpace(field.value) == "" {
					return errors.Errorf("storageSetting default S3 config.%s is required", field.name)
				}
			}
		}
	case storepb.InstanceSettingKey_MEMO_RELATED:
		if setting.GetMemoRelatedSetting() == nil {

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Add the full "s3Config" object inside the S3 storages[] entry (accessKeyId, accessKeySecret, endpoint, region, bucket).
  2. Verify the default/active storage selection: if the default should be LOCAL, fix the actived flags or storageType so the S3 entry is not the default.

Example fix

// before
"storages": [ { "id": 1, "name": "s3", "type": "S3", "actived": true } ]

// after
"storages": [ { "id": 1, "name": "s3", "type": "S3", "actived": true,
  "s3Config": { "accessKeyId": "AKIA...", "accessKeySecret": "...", "endpoint": "https://s3.amazonaws.com", "region": "us-east-1", "bucket": "memos" } } ]
Defensive patterns

Strategy: validation

Validate before calling

def := store.GetDefaultStorage(st)
if def != nil && def.Type == storepb.StorageType_STORAGE_TYPE_S3 && def.GetS3Config() == nil {
    return errors.New("default S3 storage entry is missing its s3Config")
}

Type guard

func defaultStorageIsComplete(st *storepb.InstanceStorageSetting) bool {
    def := store.GetDefaultStorage(st)
    if def == nil || def.GetType() != storepb.StorageType_STORAGE_TYPE_S3 {
        return true
    }
    return def.GetS3Config() != nil
}

Prevention

When it happens

Trigger: storageSetting.storages[] contains an entry with "type": "S3" marked active/default but the entry lacks its "s3Config" block; or the storages[] entry has a typo'd config key so protojson leaves it unset.

Common situations: Multi-storage setups migrated from the database where the s3Config nesting level changed; writing storages[] entries by hand and omitting the config; secret-mounting the credentials separately so the JSON has an empty object.

Related errors


AI-assisted analysis of usememos/memos@14d757ce1f (2026-08-15). Data as JSON: /api/errors/7e8b11cd7969753c. Report an issue: GitHub.