usememos/memos · error

storageSetting.s3Config is required for S3

Error message

storageSetting.s3Config is required for S3

What it means

Thrown when a STORAGE instance-setting file sets storageType to S3 but provides neither s3Config nor any entries in the storages list. The code comment explains the intent: normalization would silently self-heal this to LOCAL storage, so the validator fails loudly instead, preventing an operator's S3 data from silently going to local disk. Startup aborts.

Source

Thrown at store/deployment_config.go:239

func validateAndNormalizeDeploymentInstanceSetting(setting *storepb.InstanceSetting) error {
	switch setting.Key {
	case storepb.InstanceSettingKey_GENERAL:
		if setting.GetGeneralSetting() == nil {
			return errors.New("generalSetting must be populated for key GENERAL")
		}
		if offset := setting.GetGeneralSetting().WeekStartDayOffset; offset < -1 || offset > 6 {
			return errors.New("generalSetting.weekStartDayOffset must be between -1 and 6")
		}
	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},

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Provide the S3 config inline: add "s3Config": { "accessKeyId": "...", "accessKeySecret": "...", "endpoint": "...", "region": "...", "bucket": "..." } to storageSetting.
  2. Or declare the S3 storage under storageSetting.storages[] with a matching id/actived state.
  3. Or, if local storage is actually intended, set "storageType": "LOCAL".

Example fix

// before
{ "key": "STORAGE", "storageSetting": { "storageType": "S3", "uploadSizeLimitMb": 100 } }

// after
{ "key": "STORAGE", "storageSetting": { "storageType": "S3", "uploadSizeLimitMb": 100,
  "s3Config": { "accessKeyId": "AKIA...", "accessKeySecret": "...", "endpoint": "https://s3.amazonaws.com", "region": "us-east-1", "bucket": "memos" } } }
Defensive patterns

Strategy: validation

Validate before calling

st := setting.GetStorageSetting()
if st.StorageType == storepb.InstanceStorageSetting_S3 && st.S3Config == nil && len(st.Storages) == 0 {
    return errors.New("S3 storageType requires s3Config or storages entries")
}

Type guard

func s3StorageIsConfigured(st *storepb.InstanceStorageSetting) bool {
    if st.GetStorageType() != storepb.InstanceStorageSetting_S3 {
        return true
    }
    return st.GetS3Config() != nil || len(st.GetStorages()) > 0
}

Prevention

When it happens

Trigger: A memos-instance-setting-storage.json with "storageType": "S3" and no "s3Config" block and empty/missing "storages" array; partially migrated S3 config where credentials moved into storages[] but the array got dropped.

Common situations: Declaring intent ("we use S3") before filling in credentials; downgrading from a version that stored S3 config in the database and assuming the server will read it from there; typo in the s3Config key leaving it unset.

Related errors


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