{"record":{"id":"ba0d5fcb968c5af5","repo":"m1k1o/neko","slug":"unable-to-unmarshal-s-plugin-settings-from-global","errorCode":null,"errorMessage":"unable to unmarshal %s plugin settings from global settings: %w","messagePattern":"unable to unmarshal (.+?) plugin settings from global settings: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/plugins/filetransfer/manager.go","lineNumber":54,"sourceCode":"\t}\n}\n\ntype Manager struct {\n\tlogger   zerolog.Logger\n\tconfig   *Config\n\tsessions types.SessionManager\n\tshutdown chan struct{}\n\tmu       sync.RWMutex\n\tfileList []Item\n}\n\nfunc (m *Manager) isEnabledForSession(session types.Session) (bool, error) {\n\tsettings := Settings{\n\t\tEnabled: true, // defaults to true\n\t}\n\terr := m.sessions.Settings().Plugins.Unmarshal(PluginName, &settings)\n\tif err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {\n\t\treturn false, fmt.Errorf(\"unable to unmarshal %s plugin settings from global settings: %w\", PluginName, err)\n\t}\n\n\tprofile := Settings{\n\t\tEnabled: true, // defaults to true\n\t}\n\n\terr = session.Profile().Plugins.Unmarshal(PluginName, &profile)\n\tif err != nil && !errors.Is(err, types.ErrPluginSettingsNotFound) {\n\t\treturn false, fmt.Errorf(\"unable to unmarshal %s plugin settings from profile: %w\", PluginName, err)\n\t}\n\n\treturn m.config.Enabled && (settings.Enabled || session.Profile().IsAdmin) && profile.Enabled, nil\n}\n\nfunc (m *Manager) refresh() (error, bool) {\n\t// if file transfer is disabled, return immediately without refreshing\n\tif !m.config.Enabled {\n\t\treturn nil, false","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/plugins/filetransfer/manager.go#L36-L72","documentation":"Returned by filetransfer Manager.isEnabledForSession when the global settings payload for the filetransfer plugin exists but cannot be unmarshalled into the plugin's Settings struct. ErrPluginSettingsNotFound is tolerated (defaults apply); any other unmarshal error aborts with this wrapped error, so it signals malformed global plugin config, not absence of config.","triggerScenarios":"A file handler (uploadFileHandler, downloadFileHandler, deleteFileHandler) calls isEnabledForSession; sessions.Settings().Plugins.Unmarshal(\"filetransfer\", &settings) fails with an unmarshal/type error because the global settings JSON/YAML for the plugin has wrong field names or types.","commonSituations":"Admin hand-edits global config and writes enabled: \"yes\" (string) instead of a bool, or nests settings under the wrong key; a renamed Settings field after an upgrade leaves stale keys of the wrong type in the config store.","solutions":["Open global settings, find the filetransfer plugin section, and fix field names/types to match the plugin's Settings struct (Enabled bool, etc.)","Validate the settings JSON/YAML against the Settings struct (age/keys/types) before saving","Upgrade-related: migrate old config keys to the current Settings schema","Temporarily delete the plugin settings block so ErrPluginSettingsNotFound applies and defaults (Enabled=true) are used"],"exampleFix":"// before (global settings)\nplugins:\n  filetransfer:\n    enabled: \"true\"   # string, not bool\n\n// after\nplugins:\n  filetransfer:\n    enabled: true","handlingStrategy":"validation","validationCode":"raw := settings.Plugins.Get(\"filetransfer\")\nif raw != nil {\n    if _, ok := raw.(map[string]any); !ok {\n        return fmt.Errorf(\"filetransfer settings must be an object\")\n    }\n    if e, ok := raw.(map[string]any)[\"enabled\"]; ok {\n        if _, ok := e.(bool); !ok {\n            return fmt.Errorf(\"filetransfer 'enabled' must be a bool\")\n        }\n    }\n}","typeGuard":"func validFTSettings(s Settings) bool {\n    return reflect.TypeOf(s.Enabled).Kind() == reflect.Bool\n}","tryCatchPattern":"enabled, err := m.isEnabledForSession(session)\nif err != nil {\n    if errors.Is(err, types.ErrPluginSettingsNotFound) {\n        enabled = true\n    } else {\n        log.Error().Err(err).Msg(\"check filetransfer plugin settings schema\")\n        return false\n    }\n}","preventionTips":["Validate plugin settings against the Settings struct on config save, not at request time","Use JSON schema/config linting in CI for global settings","After upgrades, run a config migration for changed Settings fields"],"tags":["go","plugins","unmarshal","configuration"],"backgroundTag":"plugin-settings-unmarshal-failed","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}