usememos/memos · error

attachment storage is not configured

Error message

attachment storage is not configured

What it means

ResolveStorage (store/storage.go:212) walks a fallback chain to find the storage backend for an attachment: explicit storageID lookup in the instance setting, then the attachment's legacy S3 config, then the instance-level legacy S3 config. Reaching line 246 means storageID was empty AND no S3 config existed anywhere, so no driver can serve the attachment — errors.New("attachment storage is not configured"). Contrast with line 244, which fires when a non-empty storageID is simply absent from the setting.

Source

Thrown at store/storage.go:246

		s3Config = setting.GetS3Config()
	}
	if s3Config != nil {
		// Prefer the configured storage addressing the same namespace so legacy
		// attachments pick up rotated credentials and transport options.
		legacyStorage := &storepb.Storage{
			Type:   storepb.StorageType_STORAGE_TYPE_S3,
			Config: &storepb.Storage_S3Config{S3Config: s3Config},
		}
		if configuredStorage := findEquivalentStorage(setting.GetStorages(), legacyStorage); configuredStorage != nil {
			return configuredStorage, nil
		}
		return legacyStorage, nil
	}

	if storageID != "" {
		return nil, errors.Errorf("storage %q is not configured", storageID)
	}
	return nil, errors.New("attachment storage is not configured")
}

// ResolveStorageDriver resolves the configured storage referenced by an attachment
// and returns its driver. legacyS3Config supports attachments written before
// storage IDs were introduced.
func ResolveStorageDriver(
	ctx context.Context,
	setting *storepb.InstanceStorageSetting,
	storageID string,
	legacyS3Config *storepb.StorageS3Config,
) (storage.Driver, error) {
	resolvedStorage, err := ResolveStorage(setting, storageID, legacyS3Config)
	if err != nil {
		return nil, err
	}
	return storage.NewDriver(ctx, resolvedStorage)
}

View on GitHub (pinned to 14d757ce1f)

Solutions

  1. Configure an instance storage setting with at least one storage (a LOCAL type entry is enough) via the workspace setting API or admin UI, so attachments without an explicit storage ID resolve to the default.
  2. If the attachments were originally S3-backed, restore the legacy S3 config (workspace-level S3 setting) or add an S3 storage entry addressing the same bucket so findEquivalentStorage matches it and rotated credentials are picked up.
  3. Check the attachment row: if it should point at a specific storage, set its storage_id to a configured storage ID.
  4. Restore the workspace setting from a backup taken before the configuration was lost rather than re-creating storages with new IDs.

Example fix

// before: instance setting has no storages, attachment has no storage_id
stor, err := store.ResolveStorage(setting, att.StorageId, att.S3Config) // error

// after: ensure a default LOCAL storage exists before resolving
store.NormalizeInstanceStorageSetting(setting) // appends LOCAL fallback if empty
stor, err := store.ResolveStorage(setting, att.StorageId, att.S3Config)
Defensive patterns

Strategy: fallback

Validate before calling

// Before resolving an attachment's driver, ensure the instance can serve it:
func canResolveStorage(setting *storepb.InstanceStorageSetting, att *storepb.Attachment) bool {
    if att.GetStorageId() != "" && store.FindStorage(setting, att.GetStorageId()) != nil {
        return true
    }
    return att.GetS3Config() != nil || setting.GetS3Config() != nil || len(setting.GetStorages()) > 0
}

Try / catch

stor, err := store.ResolveStorage(setting, att.StorageId, att.S3Config)
if err != nil {
    if strings.Contains(err.Error(), "storage is not configured") {
        // surface a configuration action to the admin: set up an instance
        // storage (LOCAL or S3) before attachments can be served
    }
    return err
}

Prevention

When it happens

Trigger: Reading/downloading an attachment that has no storage_id and no legacy S3 config (i.e. it was expected to be LOCAL) while the instance storage setting contains no usable local entry; the instance setting was wiped, replaced by a config-file deployment, or restored from a backup that dropped storage configuration.

Common situations: Migrating a database between instances without copying workspace storage settings; rolling back to a config-file-based S3 setup after using the storage registry; a fresh install pointing at an old database; manually editing the workspace_setting row and losing the storages list.

Related errors


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