Skip to content

Codex: reinstall appends duplicate [[hooks.SessionStart]] when hook exists in inline form — "duplicate key" breaks config.toml #1432

Description

@astandrik

Version

  • codebase-memory-mcp 0.9.0
  • Independently reproduced with codebase-memory-mcp 0.9.1-rc.1

Platform

macOS (Apple Silicon)

Install channel

GitHub release archive / install.sh (installed to ~/.local/bin)

Binary variant

standard

What happened, and what did you expect?

codebase-memory-mcp install recognizes its managed Codex SessionStart hook by the # >>> codebase-memory-mcp SessionStart >>> marker comments. If a previously installed hook is present in ~/.codex/config.toml in a semantically equivalent inline form without those markers—for example after a settings migration or another normalization step that removes comments—the installer appends another [[hooks.SessionStart]] table.

The inline assignment already defines hooks.SessionStart, so the appended array-of-tables definition makes the TOML invalid. Codex then fails to load the entire configuration instead of starting or resuming a thread.

Expected: install/update must keep config.toml parseable and remain idempotent even when formatting or comments have changed.

Reproduction

The reproduction is sandboxed: both HOME and CODEX_HOME point to a temporary directory, so the real Codex configuration is not modified.

sandbox="$(mktemp -d)"
mkdir -p "$sandbox/.codex"

cat > "$sandbox/.codex/config.toml" <<'EOF'
[hooks]
SessionStart = [{ matcher = "startup|resume|clear|compact", hooks = [{ type = "command", command = "echo \"Code discovery: prefer codebase-memory-mcp\"" }] }]

[mcp_servers.codebase-memory-mcp]
command = "/Users/me/.local/bin/codebase-memory-mcp"
EOF

# Control: the inline-only configuration is valid and Codex can load it.
CODEX_HOME="$sandbox/.codex" codex features list >/dev/null

# Reinstall into the sandbox.
HOME="$sandbox" codebase-memory-mcp install -y

# Fails after install because hooks.SessionStart is now defined twice.
CODEX_HOME="$sandbox/.codex" codex features list

Actual result: install -y exits successfully, but appends its marked [[hooks.SessionStart]] block after the existing inline hook. The subsequent Codex command fails with duplicate key.

Control case: if config.toml contains the installer's marked block and the markers remain intact, a second install replaces/skips that block correctly and the file stays valid. The current idempotency therefore depends on the marker comments surviving.

Logs

Error from the original configuration:

ChatGPT can't load config.toml, so this thread can't resume.
Fix ~/.codex/config.toml:843:9: duplicate key.

Error from the sandboxed reproduction:

Error: <sandbox>/.codex/config.toml:10:9: duplicate key

Caused by:
    TOML parse error at line 10, column 9
       |
    10 | [[hooks.SessionStart]]
       |         ^^^^^^^^^^^^
    duplicate key

How users hit this in practice

  1. codebase-memory-mcp install adds the managed hook with marker comments.
  2. A configuration migration or normalization leaves the equivalent hook in inline SessionStart = [...] form but does not preserve the comments.
  3. A later codebase-memory-mcp install or update does not recognize the inline hook and appends another definition.
  4. Codex can no longer parse config.toml until the duplicate is repaired manually.

The exact component that performed the original normalization was not isolated; the bug does not depend on it, because the sandboxed inline fixture reproduces the installer failure directly.

Expected behavior

The installer should structurally reconcile its owned Codex lifecycle hooks across supported TOML representations. After install/update:

  • config.toml remains parseable;
  • exactly one owned SessionStart hook is present;
  • unrelated user hooks are preserved;
  • a repeated install is idempotent even if comments or formatting changed.

If ownership cannot be determined safely, the installer should fail without modifying the file and report an actionable error instead of writing invalid TOML.

Workaround

For 0.9.0, remove the newly appended marked block and keep the existing equivalent inline hook, then validate the file before restarting Codex. When updating the binary while retaining an already working configuration, the GitHub installer can be run with --skip-config to avoid rewriting Codex configuration.

Related issues

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable synthetic configuration, not proprietary code or logs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    editor/integrationEditor compatibility and CLI integration

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions