Repository: meltano/meltano

Issue:
bug: Error when setting metadata is `None`
Was testing the superset_ext and came across this bug. It did successfully set and only seemed to happen when the key wasn't present in the config.

Make sure you scroll in the code block.

```code
❯ meltano --log-level=debug config superset_ext set SECRET_KEY $(openssl rand -base64 42)

2022-10-25T19:59:26.783439Z [debug    ] Creating engine '<meltano.core.project.Project object at 0x104463700>@sqlite:////Users/taylor/Documents/Projects/dev/meltano/supersettest/.meltano/meltano.db'
2022-10-25T19:59:26.827219Z [info     ] The default environment 'dev' will be ignored for `meltano config`. To configure a specific environment, please use the option `--environment=<environment name>`.
2022-10-25T19:59:27.904933Z [debug    ] AutoStoreManager returned store 'meltano_yml'
Utility 'superset_ext' setting 'SECRET_KEY' was set in `meltano.yml`: 'LgrtgYvKfYccH9UzvrqJawiT9OHx8JTO7G9ALyF+LWiuKZlpMYur/Uh3'
❯ meltano --log-level=debug config superset_ext set SECRET_KEY $(openssl rand -base64 42)

2022-10-25T19:59:40.250843Z [debug    ] Creating engine '<meltano.core.project.Project object at 0x104443700>@sqlite:////Users/taylor/Documents/Projects/dev/meltano/supersettest/.meltano/meltano.db'
2022-10-25T19:59:40.319847Z [info     ] The default environment 'dev' will be ignored for `meltano config`. To configure a specific environment, please use the option `--environment=<environment name>`.
2022-10-25T19:59:41.605915Z [debug    ] AutoStoreManager returned store 'meltano_yml'
2022-10-25T19:59:41.659145Z [debug    ] Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'NoneType' object has no attribute 'is_redacted' 
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/__init__.py:95 in _run_cli          │
│                                                                                                  │
│    92 │   """                                                                                    │
│    93 │   try:                                                                                   │
│    94 │   │   try:  # noqa: WPS225, WPS505                                                       │
│ ❱  95 │   │   │   cli(obj={"project": None})                                                     │
│    96 │   │   except ProjectReadonly as err:                                                     │
│    97 │   │   │   raise CliError(                                                                │
│    98 │   │   │   │   f"The requested action could not be completed: {err}"                      │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ cli_error = CliError("Need help fixing this problem? Visit http://melta.no/ for              │ │
│ │             troubleshooting steps, or to\njoin our friendly Slack community.\n\n'NoneType'   │ │
│ │             object has no attribute 'is_redacted'")                                          │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/taylor/Library/Caches/pypoetry/virtualenvs/meltano-1RhssGgR-py3.9/lib/python3.9/site-pack │
│ ages/click/core.py:1130 in __call__                                                              │
│                                                                                                  │
│   1127 │                                                                                         │
│   1128 │   def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any:                           │
│   1129 │   │   """Alias for :meth:`main`."""                                                     │
│ ❱ 1130 │   │   return self.main(*args, **kwargs)                                                 │
│   1131                                                                                           │
│   1132                                                                                           │
│   1133 class Command(BaseCommand):                                                               │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │   args = ()                                                                                  │ │
│ │ kwargs = {                                                                                   │ │
│ │          │   'obj': {                                                                        │ │
│ │          │   │   'project': <meltano.core.project.Project object at 0x104443700>,            │ │
│ │          │   │   'verbosity': 0,                                                             │ │
│ │          │   │   'selected_environment': 'dev',                                              │ │
│ │          │   │   'is_default_environment': True,                                             │ │
│ │          │   │   'tracker': <meltano.core.tracking.tracker.Tracker object at 0x1044d4220>,   │ │
│ │          │   │   'settings': <meltano.core.plugin.settings_service.PluginSettingsService     │ │
│ │          object at 0x10491c2b0>,                                                             │ │
│ │          │   │   'session': <sqlalchemy.orm.session.Session object at 0x1048d4be0>,          │ │
│ │          │   │   'invoker': <meltano.core.plugin_invoker.PluginInvoker object at             │ │
│ │          0x1021de070>                                                                        │ │
│ │          │   }                                                                               │ │
│ │          }                                                                                   │ │
│ │   self = <NoWindowsGlobbingGroup cli>                                                        │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/cli.py:39 in main                   │
│                                                                                                  │
│    36 │   │   │   args: Positional arguments for the Click group.                                │
│    37 │   │   │   kwargs: Keyword arguments for the Click group.                                 │
│    38 │   │   """                                                                                │
│ ❱  39 │   │   return super().main(*args, windows_expand_args=False, **kwargs)                    │
│    40                                                                                            │
│    41                                                                                            │
│    42 @click.group(                                                                              │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ __class__ = <class 'meltano.cli.cli.NoWindowsGlobbingGroup'>                                 │ │
│ │      args = ()                                                                               │ │
│ │    kwargs = {                                                                                │ │
│ │             │   'obj': {                                                                     │ │
│ │             │   │   'project': <meltano.core.project.Project object at 0x104443700>,         │ │
│ │             │   │   'verbosity': 0,                                                          │ │
│ │             │   │   'selected_environment': 'dev',                                           │ │
│ │             │   │   'is_default_environment': True,                                          │ │
│ │             │   │   'tracker': <meltano.core.tracking.tracker.Tracker object at              │ │
│ │             0x1044d4220>,                                                                    │ │
│ │             │   │   'settings': <meltano.core.plugin.settings_service.PluginSettingsService  │ │
│ │             object at 0x10491c2b0>,                                                          │ │
│ │             │   │   'session': <sqlalchemy.orm.session.Session object at 0x1048d4be0>,       │ │
│ │             │   │   'invoker': <meltano.core.plugin_invoker.PluginInvoker object at          │ │
│ │             0x1021de070>                                                                     │ │
│ │             │   }                                                                            │ │
│ │             }                                                                                │ │
│ │      self = <NoWindowsGlobbingGroup cli>                                                     │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/taylor/Library/Caches/pypoetry/virtualenvs/meltano-1RhssGgR-py3.9/lib/python3.9/site-pack │
│ ages/click/core.py:1055 in main                                                                  │
│                                                                                                  │
│   1052 │   │   try:                                                                              │
│   1053 │   │   │   try:                                                                          │
│   1054 │   │   │   │   with self.make_context(prog_name, args, **extra) as ctx:                  │
│ ❱ 1055 │   │   │   │   │   rv = self.invoke(ctx)                                                 │
│   1056 │   │   │   │   │   if not standalone_mode:                                               │
│   1057 │   │   │   │   │   │   return rv                                                         │
│   1058 │   │   │   │   │   # it's not safe to `ctx.exit(rv)` here!                               │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │                args = [                                                                      │ │
│ │                       │   'config',                                                          │ │
│ │                       │   'superset_ext',                                                    │ │
│ │                       │   'set',                                                             │ │
│ │                       │   'SECRET_KEY',                                                      │ │
│ │                       │   'lWYHzhR2JX4u+wZK1PPXQ675mQwa7lVoYlbIJtqvrzskOCjqoiMjJtmN'         │ │
│ │                       ]                                                                      │ │
│ │        complete_var = None                                                                   │ │
│ │                 ctx = <click.core.Context object at 0x100889fd0>                             │ │
│ │               extra = {                                                                      │ │
│ │                       │   'obj': {                                                           │ │
│ │                       │   │   'project': <meltano.core.project.Project object at             │ │
│ │                       0x104443700>,                                                          │ │
│ │                       │   │   'verbosity': 0,                                                │ │
│ │                       │   │   'selected_environment': 'dev',                                 │ │
│ │                       │   │   'is_default_environment': True,                                │ │
│ │                       │   │   'tracker': <meltano.core.tracking.tracker.Tracker object at    │ │
│ │                       0x1044d4220>,                                                          │ │
│ │                       │   │   'settings':                                                    │ │
│ │                       <meltano.core.plugin.settings_service.PluginSettingsService object at  │ │
│ │                       0x10491c2b0>,                                                          │ │
│ │                       │   │   'session': <sqlalchemy.orm.session.Session object at           │ │
│ │                       0x1048d4be0>,                                                          │ │
│ │                       │   │   'invoker': <meltano.core.plugin_invoker.PluginInvoker object   │ │
│ │                       at 0x1021de070>                                                        │ │
│ │                       │   }                                                                  │ │
│ │                       }                                                                      │ │
│ │           prog_name = 'meltano'                                                              │ │
│ │                self = <NoWindowsGlobbingGroup cli>                                           │ │
│ │     standalone_mode = True                                                                   │ │
│ │ windows_expand_args = False                                                                  │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/utils.py:541 in invoke              │
│                                                                                                  │
│   538 │   │   ctx.ensure_object(dict)                                                            │
│   539 │   │   if ctx.obj.get("tracker"):                                                         │
│   540 │   │   │   ctx.obj["tracker"].add_contexts(CliContext.from_click_context(ctx))            │
│ ❱ 541 │   │   super().invoke(ctx)  # noqa: WPS608                                                │
│   542                                                                                            │
│   543                                                                                            │
│   544 class InstrumentedCmd(click.Command):                                                      │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │ __class__ = <class 'meltano.cli.utils.InstrumentedGroup'> │                                    │
│ │       ctx = <click.core.Context object at 0x100889fd0>    │                                    │
│ │      self = <NoWindowsGlobbingGroup cli>                  │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /Users/taylor/Library/Caches/pypoetry/virtualenvs/meltano-1RhssGgR-py3.9/lib/python3.9/site-pack │
│ ages/click/core.py:1657 in invoke                                                                │
│                                                                                                  │
│   1654 │   │   │   │   super().invoke(ctx)                                                       │
│   1655 │   │   │   │   sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)                    │
│   1656 │   │   │   │   with sub_ctx:                                                             │
│ ❱ 1657 │   │   │   │   │   return _process_result(sub_ctx.command.invoke(sub_ctx))               │
│   1658 │   │                                                                                     │
│   1659 │   │   # In chain mode we create the contexts step by step, but after the                │
│   1660 │   │   # base command has been invoked.  Because at that point we do not                 │
│                                                                                                  │
│ ╭───────────────────────────────────────── locals ─────────────────────────────────────────╮     │
│ │       __class__ = <class 'click.core.MultiCommand'>                                      │     │
│ │ _process_result = <function MultiCommand.invoke.<locals>._process_result at 0x1006a09d0> │     │
│ │            args = [                                                                      │     │
│ │                   │   'superset_ext',                                                    │     │
│ │                   │   'set',                                                             │     │
│ │                   │   'SECRET_KEY',                                                      │     │
│ │                   │   'lWYHzhR2JX4u+wZK1PPXQ675mQwa7lVoYlbIJtqvrzskOCjqoiMjJtmN'         │     │
│ │                   ]                                                                      │     │
│ │             cmd = <InstrumentedGroup config>                                             │     │
│ │        cmd_name = 'config'                                                               │     │
│ │             ctx = <click.core.Context object at 0x100889fd0>                             │     │
│ │            self = <NoWindowsGlobbingGroup cli>                                           │     │
│ │         sub_ctx = <click.core.Context object at 0x1044438e0>                             │     │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯     │
│                                                                                                  │
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/utils.py:541 in invoke              │
│                                                                                                  │
│   538 │   │   ctx.ensure_object(dict)                                                            │
│   539 │   │   if ctx.obj.get("tracker"):                                                         │
│   540 │   │   │   ctx.obj["tracker"].add_contexts(CliContext.from_click_context(ctx))            │
│ ❱ 541 │   │   super().invoke(ctx)  # noqa: WPS608                                                │
│   542                                                                                            │
│   543                                                                                            │
│   544 class InstrumentedCmd(click.Command):                                                      │
│                                                                                                  │
│ ╭───────────────────────── locals ──────────────────────────╮                                    │
│ │ __class__ = <class 'meltano.cli.utils.InstrumentedGroup'> │                                    │
│ │       ctx = <click.core.Context object at 0x1044438e0>    │                                    │
│ │      self = <InstrumentedGroup config>                    │                                    │
│ ╰───────────────────────────────────────────────────────────╯                                    │
│                                                                                                  │
│ /Users/taylor/Library/Caches/pypoetry/virtualenvs/meltano-1RhssGgR-py3.9/lib/python3.9/site-pack │
│ ages/click/core.py:1657 in invoke                                                                │
│                                                                                                  │
│   1654 │   │   │   │   super().invoke(ctx)                                                       │
│   1655 │   │   │   │   sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)                    │
│   1656 │   │   │   │   with sub_ctx:                                                             │
│ ❱ 1657 │   │   │   │   │   return _process_result(sub_ctx.command.invoke(sub_ctx))               │
│   1658 │   │                                                                                     │
│   1659 │   │   # In chain mode we create the contexts step by step, but after the                │
│   1660 │   │   # base command has been invoked.  Because at that point we do not                 │
│                                                                                                  │
│ ╭───────────────────────────────────────── locals ─────────────────────────────────────────╮     │
│ │       __class__ = <class 'click.core.MultiCommand'>                                      │     │
│ │ _process_result = <function MultiCommand.invoke.<locals>._process_result at 0x104459ee0> │     │
│ │            args = []                                                                     │     │
│ │             cmd = <PartialInstrumentedCmd set>                                           │     │
│ │        cmd_name = 'set'                                                                  │     │
│ │             ctx = <click.core.Context object at 0x1044438e0>                             │     │
│ │            self = <InstrumentedGroup config>                                             │     │
│ │         sub_ctx = <click.core.Context object at 0x104536670>                             │     │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────╯     │
│                                                                                                  │
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/utils.py:577 in invoke              │
│                                                                                                  │
│   574 │   │   if ctx.obj.get("tracker"):                                                         │
│   575 │   │   │   ctx.obj["tracker"].add_contexts(CliContext.from_click_context(ctx))            │
│   576 │   │   │   ctx.obj["tracker"].track_command_event(CliEvent.started)                       │
│ ❱ 577 │   │   super().invoke(ctx)  # noqa: WPS608                                                │
│   578                                                                                            │
│                                                                                                  │
│ ╭──────────────────────────── locals ────────────────────────────╮                               │
│ │ __class__ = <class 'meltano.cli.utils.PartialInstrumentedCmd'> │                               │
│ │       ctx = <click.core.Context object at 0x104536670>         │                               │
│ │      self = <PartialInstrumentedCmd set>                       │                               │
│ ╰────────────────────────────────────────────────────────────────╯                               │
│                                                                                                  │
│ /Users/taylor/Library/Caches/pypoetry/virtualenvs/meltano-1RhssGgR-py3.9/lib/python3.9/site-pack │
│ ages/click/core.py:1404 in invoke                                                                │
│                                                                                                  │
│   1401 │   │   │   echo(style(message, fg="red"), err=True)                                      │
│   1402 │   │                                                                                     │
│   1403 │   │   if self.callback is not None:                                                     │
│ ❱ 1404 │   │   │   return ctx.invoke(self.callback, **ctx.params)                                │
│   1405 │                                                                                         │
│   1406 │   def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]   │
│   1407 │   │   """Return a list of completions for the incomplete value. Looks                   │
│                                                                                                  │
│ ╭───────────────────── locals ──────────────────────╮                                            │
│ │  ctx = <click.core.Context object at 0x104536670> │                                            │
│ │ self = <PartialInstrumentedCmd set>               │                                            │
│ ╰───────────────────────────────────────────────────╯                                            │
│                                                                                                  │
│ /Users/taylor/Library/Caches/pypoetry/virtualenvs/meltano-1RhssGgR-py3.9/lib/python3.9/site-pack │
│ ages/click/core.py:760 in invoke                                                                 │
│                                                                                                  │
│    757 │   │                                                                                     │
│    758 │   │   with augment_usage_errors(__self):                                                │
│    759 │   │   │   with ctx:                                                                     │
│ ❱  760 │   │   │   │   return __callback(*args, **kwargs)                                        │
│    761 │                                                                                         │
│    762 │   def forward(                                                                          │
│    763 │   │   __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any  # noqa: B902             │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ _Context__callback = <function set_ at 0x104258b80>                                          │ │
│ │     _Context__self = <click.core.Context object at 0x104536670>                              │ │
│ │               args = ()                                                                      │ │
│ │                ctx = <click.core.Context object at 0x104536670>                              │ │
│ │             kwargs = {                                                                       │ │
│ │                      │   'setting_name': ('SECRET_KEY',),                                    │ │
│ │                      │   'value':                                                            │ │
│ │                      'lWYHzhR2JX4u+wZK1PPXQ675mQwa7lVoYlbIJtqvrzskOCjqoiMjJtmN',             │ │
│ │                      │   'interactive': False,                                               │ │
│ │                      │   'store': <SettingValueStore.AUTO: 'auto'>                           │ │
│ │                      }                                                                       │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /Users/taylor/Library/Caches/pypoetry/virtualenvs/meltano-1RhssGgR-py3.9/lib/python3.9/site-pack │
│ ages/click/decorators.py:26 in new_func                                                          │
│                                                                                                  │
│    23 │   """                                                                                    │
│    24 │                                                                                          │
│    25 │   def new_func(*args, **kwargs):  # type: ignore                                         │
│ ❱  26 │   │   return f(get_current_context(), *args, **kwargs)                                   │
│    27 │                                                                                          │
│    28 │   return update_wrapper(t.cast(F, new_func), f)                                          │
│    29                                                                                            │
│                                                                                                  │
│ ╭───────────────────────────────────── locals ──────────────────────────────────────╮            │
│ │   args = ()                                                                       │            │
│ │      f = <function set_ at 0x104258af0>                                           │            │
│ │ kwargs = {                                                                        │            │
│ │          │   'setting_name': ('SECRET_KEY',),                                     │            │
│ │          │   'value': 'lWYHzhR2JX4u+wZK1PPXQ675mQwa7lVoYlbIJtqvrzskOCjqoiMjJtmN', │            │
│ │          │   'interactive': False,                                                │            │
│ │          │   'store': <SettingValueStore.AUTO: 'auto'>                            │            │
│ │          }                                                                        │            │
│ ╰───────────────────────────────────────────────────────────────────────────────────╯            │
│                                                                                                  │
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/config.py:289 in set_               │
│                                                                                                  │
│   286 │   if interactive:                                                                        │
│   287 │   │   interaction.configure_all()                                                        │
│   288 │   else:                                                                                  │
│ ❱ 289 │   │   interaction.set_value(setting_name=setting_name, value=value, store=store)         │
│   290                                                                                            │
│   291                                                                                            │
│   292 @config.command(cls=PartialInstrumentedCmd, name="test")                                   │
│                                                                                                  │
│ ╭──────────────────────────────────────── locals ─────────────────────────────────────────╮      │
│ │          ctx = <click.core.Context object at 0x104536670>                               │      │
│ │  interaction = <meltano.cli.interactive.config.InteractiveConfig object at 0x104a7e400> │      │
│ │  interactive = False                                                                    │      │
│ │ setting_name = ('SECRET_KEY',)                                                          │      │
│ │        store = <SettingValueStore.AUTO: 'auto'>                                         │      │
│ │        value = 'lWYHzhR2JX4u+wZK1PPXQ675mQwa7lVoYlbIJtqvrzskOCjqoiMjJtmN'               │      │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────╯      │
│                                                                                                  │
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/interactive/config.py:385 in        │
│ set_value                                                                                        │
│                                                                                                  │
│   382 │   │                                                                                      │
│   383 │   │   name = metadata["name"]                                                            │
│   384 │   │   store = metadata["store"]                                                          │
│ ❱ 385 │   │   if metadata["setting"].is_redacted:                                                │
│   386 │   │   │   value = REDACTED_VALUE                                                         │
│   387 │   │   click.secho(                                                                       │
│   388 │   │   │   f"{settings.label.capitalize()} setting '{name}' was set in {store.label}: {   │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │  interactive = False                                                                         │ │
│ │     metadata = {                                                                             │ │
│ │                │   'name': 'SECRET_KEY',                                                     │ │
│ │                │   'path': ['SECRET_KEY'],                                                   │ │
│ │                │   'store': <SettingValueStore.MELTANO_YML: 'meltano_yml'>,                  │ │
│ │                │   'setting': None                                                           │ │
│ │                }                                                                             │ │
│ │         name = 'SECRET_KEY'                                                                  │ │
│ │         path = ['SECRET_KEY']                                                                │ │
│ │         self = <meltano.cli.interactive.config.InteractiveConfig object at 0x104a7e400>      │ │
│ │ setting_name = ('SECRET_KEY',)                                                               │ │
│ │     settings = <meltano.core.plugin.settings_service.PluginSettingsService object at         │ │
│ │                0x10491c2b0>                                                                  │ │
│ │        store = <SettingValueStore.MELTANO_YML: 'meltano_yml'>                                │ │
│ │        value = 'lWYHzhR2JX4u+wZK1PPXQ675mQwa7lVoYlbIJtqvrzskOCjqoiMjJtmN'                    │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
AttributeError: 'NoneType' object has no attribute 'is_redacted'

The above exception was the direct cause of the following exception:

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /Users/taylor/Documents/Projects/dev/meltano/src/meltano/cli/__init__.py:105 in _run_cli         │
│                                                                                                  │
│   102 │   │   except MeltanoError as err:                                                        │
│   103 │   │   │   handle_meltano_error(err)                                                      │
│   104 │   │   except Exception as err:                                                           │
│ ❱ 105 │   │   │   raise CliError(f"{troubleshooting_message}\n{err}") from err                   │
│   106 │   except CliError as cli_error:                                                          │
│   107 │   │   cli_error.print()                                                                  │
│   108 │   │   sys.exit(1)                                                                        │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │ cli_error = CliError("Need help fixing this problem? Visit http://melta.no/ for              │ │
│ │             troubleshooting steps, or to\njoin our friendly Slack community.\n\n'NoneType'   │ │
│ │             object has no attribute 'is_redacted'")                                          │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
CliError: Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'NoneType' object has no attribute 'is_redacted'

Need help fixing this problem? Visit http://melta.no/ for troubleshooting steps, or to
join our friendly Slack community.

'NoneType' object has no attribute 'is_redacted'
```


Discussion:
This _could_ have something to do with having a `config` block already existing? 

```yaml
plugins:
  utilities:
  - name: superset_ext
    namespace: superset
    pip_url: apache-superset==2.0.0 flask==2.0.3 werkzeug==2.0.3 jinja2==3.0.1 wtforms==2.3.3
      git+https://github.com/meltano/superset-ext.git@feat/init-extension-take2
    executable: superset_invoker
    commands:
      describe:
        args: describe
        executable: superset_extension
      initialize:
        args: initialize
        executable: superset_extension
      create_admin:
        args: create_admin
        executable: superset_extension
      ui: run --port 8088 --host 127.0.0.1
    config:
      ROW_LIMIT: 5000
      SQLALCHEMY_DATABASE_URI: sqlite:///$MELTANO_PROJECT_ROOT/superset/superset.db
      SUPERSET_WEBSERVER_PORT: 8088
      SUPERSET_CONFIG_PATH: $MELTANO_PROJECT_ROOT/superset/superset_config.py
      FLASK_APP: superset
```
this was my meltano.yml before applying the command.
@tayloramurphy I just hit this too, and have a fix ready.

Resolving patch:
diff --git a/src/meltano/cli/interactive/config.py b/src/meltano/cli/interactive/config.py
--- a/src/meltano/cli/interactive/config.py
+++ b/src/meltano/cli/interactive/config.py
@@ -16,7 +16,13 @@
 from meltano.cli.interactive.utils import InteractionStatus
 from meltano.cli.utils import CliError
 from meltano.core.environment_service import EnvironmentService
-from meltano.core.settings_service import REDACTED_VALUE, SettingKind, SettingValueStore
+from meltano.core.project import Project
+from meltano.core.settings_service import (
+    REDACTED_VALUE,
+    SettingKind,
+    SettingsService,
+    SettingValueStore,
+)
 from meltano.core.settings_store import StoreNotSupportedError
 from meltano.core.tracking import CliEvent
 
@@ -60,8 +66,8 @@ def __init__(self, ctx, store, extras=False, max_width=None):
         self.ctx = ctx
         self.store = store
         self.extras = extras
-        self.project = self.ctx.obj["project"]
-        self.settings = self.ctx.obj["settings"]
+        self.project: Project = self.ctx.obj["project"]
+        self.settings: SettingsService = self.ctx.obj["settings"]
         self.session = self.ctx.obj["session"]
         self.tracker = self.ctx.obj["tracker"]
         self.environment_service = EnvironmentService(self.project)
@@ -382,7 +388,8 @@ def set_value(self, setting_name, value, store, interactive=False):
 
         name = metadata["name"]
         store = metadata["store"]
-        if metadata["setting"].is_redacted:
+        is_redacted = metadata["setting"] and metadata["setting"].is_redacted
+        if is_redacted:
             value = REDACTED_VALUE
         click.secho(
             f"{settings.label.capitalize()} setting '{name}' was set in {store.label}: {value!r}",
@@ -391,7 +398,7 @@ def set_value(self, setting_name, value, store, interactive=False):
 
         current_value, source = settings.get_with_source(name, session=self.session)
         if source != store:
-            if metadata["setting"].is_redacted:
+            if is_redacted:
                 current_value = REDACTED_VALUE
             click.secho(
                 f"Current value is still: {current_value!r} (from {source.label})",
diff --git a/src/meltano/core/settings_service.py b/src/meltano/core/settings_service.py
--- a/src/meltano/core/settings_service.py
+++ b/src/meltano/core/settings_service.py
@@ -141,7 +141,7 @@ def db_namespace(self) -> str:
     def setting_definitions(self) -> list[SettingDefinition]:
         """Return definitions of supported settings."""
 
-    @property
+    @property  # noqa: B027
     def inherited_settings_service(self):
         """Return settings service to inherit configuration from."""
 
@@ -231,11 +231,10 @@ def config_with_metadata(
                 **kwargs,
             )
 
-            name = setting_def.name
-            if prefix:
-                name = name[len(prefix) :]  # noqa: E203
-
-            config[name] = {**metadata, "value": value}
+            config[setting_def.name[len(prefix) :] if prefix else setting_def.name] = {
+                **metadata,
+                "value": value,
+            }
 
         return config
 
@@ -275,10 +274,8 @@ def as_env(self, *args, **kwargs) -> dict[str, str]:
         Returns:
             settings as environment variables
         """
-        full_config = self.config_with_metadata(*args, **kwargs)
-
         env = {}
-        for _, config in full_config.items():
+        for _, config in self.config_with_metadata(*args, **kwargs).items():
             value = config["value"]
             if value is None:
                 continue
@@ -442,8 +439,8 @@ def get(self, *args, **kwargs):
         value, _ = self.get_with_source(*args, **kwargs)
         return value
 
-    def set_with_metadata(  # noqa: WPS615
-        self, path: list[str], value, store=SettingValueStore.AUTO, **kwargs
+    def set_with_metadata(  # noqa: WPS615, WPS210
+        self, path: str | list[str], value, store=SettingValueStore.AUTO, **kwargs
     ):
         """Set the value and metadata for a setting.
 
@@ -466,6 +463,7 @@ def set_with_metadata(  # noqa: WPS615
         try:
             setting_def = self.find_setting(name)
         except SettingMissingError:
+            warnings.warn(f"Unknown setting {name!r}", RuntimeWarning)
             setting_def = None
 
         metadata = {"name": name, "path": path, "store": store, "setting": setting_def}
@@ -480,11 +478,13 @@ def set_with_metadata(  # noqa: WPS615
                 metadata["uncast_value"] = value
                 value = cast_value
 
-        manager = store.manager(self, **kwargs)
-        set_metadata = manager.set(name, path, value, setting_def=setting_def)
-        metadata.update(set_metadata)
+        metadata.update(
+            store.manager(self, **kwargs).set(
+                name, path, value, setting_def=setting_def
+            )
+        )
 
-        self.log(f"Set setting '{name}' with metadata: {metadata}")
+        self.log(f"Set setting {name!r} with metadata: {metadata}")
         return value, metadata
 
     def set(self, *args, **kwargs):
@@ -523,13 +523,15 @@ def unset(self, path: list[str], store=SettingValueStore.AUTO, **kwargs):
         except SettingMissingError:
             setting_def = None
 
-        metadata = {"name": name, "path": path, "store": store, "setting": setting_def}
-
-        manager = store.manager(self, **kwargs)
-        unset_metadata = manager.unset(name, path, setting_def=setting_def)
-        metadata.update(unset_metadata)
+        metadata = {
+            "name": name,
+            "path": path,
+            "store": store,
+            "setting": setting_def,
+            **store.manager(self, **kwargs).unset(name, path, setting_def=setting_def),
+        }
 
-        self.log(f"Unset setting '{name}' with metadata: {metadata}")
+        self.log(f"Unset setting {name!r} with metadata: {metadata}")
         return metadata
 
     def reset(self, store=SettingValueStore.AUTO, **kwargs):


Test patch: