[BUG] Mods: $.config.set({ key: 'theme' }) returns success but the running session's theme and $.config.list() don't change

Status Open
Reported on v2.1.300
Maintainer reply None cached
Activity 0 comments · opened Oct 15, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Summary

A plugin calling $.config.set({ key: 'theme', value }) gets back { value } with no deny, and ~/.claude/settings.json is rewritten with the new value. But nothing changes in the running session:

  • the rendered palette stays the same (including Code elements in a plugin Pane, both format: 'diff' and plain source)
  • $.config.list() called right after the set, in the same session, still reports the old value for the theme row

The plugin API docs say $.config.set "changes one row as if the person did in the menu". Changing Theme in the /config menu applies right away. So from a plugin's side, the call looks successful but does nothing until restart.

Possibly the same root cause as #98161 (theme in settings.json not applied to a running session). This report covers the plugin API path: the API reports success, and its own list() contradicts the set.

Environment

  • Claude Code 2.1.300
  • macOS (Darwin 24.6.0)
  • Terminal: IntelliJ IDEA built-in terminal (IntelliJ IDEA 2026.x.x, TERM=xterm-256color)
  • Reproduced in an interactive session and in claude -p

Reproduction

Minimal plugin, hooks/register.ts:

import type { Register } from 'claude-code'

export const register: Register = on => {
  on('session.start', async ($, e, next) => {
    const theme = async () => (await $.config.list()).find(r => r.key === 'theme')?.value
    const log: unknown[] = [['before', await theme()]]
    log.push(['set dark', await $.config.set({ key: 'theme', value: 'dark' })])
    log.push(['after', await theme()])
    console.error(JSON.stringify(log))
    return next(e)
  })
}

Start with "theme": "light" in settings, then run claude --plugin-dir <dir>.

Actual

[["before","light"],["set dark",{"value":"dark"}],["after","light"]]
  • ~/.claude/settings.json now contains "theme": "dark"
  • The session keeps the light palette, and list() still says light

In an interactive session, a plugin Pane button that toggles the theme this way updates its own label, but the diff colors in the pane never change.

Expected

One of:

  1. The theme applies to the running session, the same as picking it in /config, and list() reflects it; or
  2. If the theme can't be changed live from a plugin, $.config.set returns { deny: '...' } instead of reporting success.

Why it matters

The plugin API leaves all Code colors to the engine ("the colour on screen is the engine's, never the plugin's"). Changing the theme is the only way a plugin can offer a light/dark switch for diffs it renders.

What Should Happen?

$.config.set({ key: 'theme', value }) from a plugin should behave like picking the theme in the /config menu: the running session repaints with the new theme immediately (including Code elements in plugin Panes), and $.config.list() reports the new value right after the call.

If changing the theme from a plugin isn't supported in a running session, $.config.set should return { deny: '<reason>' } instead of { value }, so the plugin can tell the user rather than silently doing nothing.

Error Messages/Logs

Steps to Reproduce

  1. Set the theme to light with /config → Theme → light, so ~/.claude/settings.json has "theme": "light".
  2. Create a minimal plugin folder theme-repro/ with these three files:

theme-repro/.claude-plugin/plugin.json
``json
{ "name": "theme-repro", "version": "0.0.1" }
``

theme-repro/hooks/hooks.json
``json
{ "modules": ["./register.ts"] }
``

theme-repro/hooks/register.ts
```ts
import type { Register } from 'claude-code'

export const register: Register = on => {
on('session.start', async ($, e, next) => {
const theme = async () => (await $.config.list()).find(row => row.key === 'theme')?.value
const log: unknown[] = [['before', await theme()]]
log.push(['set dark', await $.config.set({ key: 'theme', value: 'dark' })])
log.push(['after', await theme()])
await $.process.run(['sh', '-c', 'printf %s "$1" > /tmp/theme-repro.json', 'sh', JSON.stringify(log)])
return next(e)
})
}
```

  1. Run claude --plugin-dir ./theme-repro (interactive). claude -p "say ok" --plugin-dir ./theme-repro shows the same log.
  2. Check the log: cat /tmp/theme-repro.json

``
[["before","light"],["set dark",{"value":"dark"}],["after","light"]]
`
set reports success, but list() still returns light` right after it.

  1. Check the settings file: grep '"theme"' ~/.claude/settings.json now shows "theme": "dark".
  2. Look at the interactive session from step 3: it is still drawn with the light palette. Code blocks and diffs keep their light colors for the rest of the session. The dark theme only appears after restarting Claude Code.
  3. For comparison, change Theme in /config by hand: the session repaints immediately.

Claude Model

_No response_

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.300 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

IntelliJ IDEA terminal

Additional Information

_No response_

View original on GitHub ↗