m1k1o/neko · error

unable to unmarshal %s plugin settings from profile: %w

Error message

unable to unmarshal %s plugin settings from profile: %w

What it means

Companion to error 62 but scoped to the session's profile: isEnabledForSession fails when session.Profile().Plugins.Unmarshal("filetransfer", &profile) errors for reasons other than settings-not-found. It means the per-profile filetransfer settings block is present but does not match the plugin's Settings schema.

Source

Thrown at server/internal/plugins/filetransfer/manager.go:63

	fileList []Item
}

func (m *Manager) isEnabledForSession(session types.Session) (bool, error) {
	settings := Settings{
		Enabled: true, // defaults to true
	}
	err := m.sessions.Settings().Plugins.Unmarshal(PluginName, &settings)
	if err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {
		return false, fmt.Errorf("unable to unmarshal %s plugin settings from global settings: %w", PluginName, err)
	}

	profile := Settings{
		Enabled: true, // defaults to true
	}

	err = session.Profile().Plugins.Unmarshal(PluginName, &profile)
	if err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {
		return false, fmt.Errorf("unable to unmarshal %s plugin settings from profile: %w", PluginName, err)
	}

	return m.config.Enabled && (settings.Enabled || session.Profile().IsAdmin) && profile.Enabled, nil
}

func (m *Manager) refresh() (error, bool) {
	// if file transfer is disabled, return immediately without refreshing
	if !m.config.Enabled {
		return nil, false
	}

	files, err := ListFiles(m.config.RootDir)
	if err != nil {
		return err, false
	}

	m.mu.Lock()
	defer m.mu.Unlock()

View on GitHub (pinned to b0f01cedea)

Solutions

  1. Fix the filetransfer section in the user's profile settings to match the current Settings struct (Enabled bool)
  2. Re-export/recreate the profile from a known-good template for this server version
  3. As a stopgap, remove the plugin block from the profile so the not-found default (Enabled=true) applies
  4. Check server/plugin version mismatch: a profile created by a newer/older version may have an incompatible schema

Example fix

// before (profile)
"plugins": { "filetransfer": { "enabled": "on" } }

// after
"plugins": { "filetransfer": { "enabled": true } }
Defensive patterns

Strategy: validation

Validate before calling

raw := session.Profile().Plugins.Get("filetransfer")
if raw != nil {
    var s Settings
    if err := mapToStruct(raw, &s); err != nil {
        return fmt.Errorf("profile filetransfer settings invalid: %w", err)
    }
}

Try / catch

enabled, err := m.isEnabledForSession(session)
if err != nil {
    if errors.Is(err, types.ErrPluginSettingsNotFound) {
        enabled = true
    } else {
        log.Error().Err(err).Str("profile", session.Profile().Name()).Msg("malformed profile plugin settings")
        return false
    }
}

Prevention

When it happens

Trigger: Any file upload/download/delete request whose session profile contains a filetransfer plugin section with malformed fields (wrong types, unknown structure), causing Profile().Plugins.Unmarshal to return a non-ErrPluginSettingsNotFound error.

Common situations: Profile JSON edited by hand or imported from another deployment with an incompatible Settings shape; a profile exported from an older server version where Settings fields changed.

Related errors


AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01). Data as JSON: /api/errors/997f47856fe277ff. Report an issue: GitHub.