Skip to content

fix(core): preserve config.toml symlinks on write#9437

Closed
ryoppippi wants to merge 1 commit intoopenai:mainfrom
ryoppippi:fix/config-symlink-write
Closed

fix(core): preserve config.toml symlinks on write#9437
ryoppippi wants to merge 1 commit intoopenai:mainfrom
ryoppippi:fix/config-symlink-write

Conversation

@ryoppippi
Copy link

@ryoppippi ryoppippi commented Jan 18, 2026

Fixes #6646

What

When Codex writes to ~/.codex/config.toml, we persist via a temporary file + rename. If config.toml is a symlink (e.g. managed externally), renaming over it replaces the symlink with a regular file.

This PR follows the symlink chain and atomically writes to the final target file instead, preserving the symlink.

Changes

  • Follow config.toml symlink chain and write to the final target path
  • Create the temp file in the target directory so the atomic rename works
  • Return a clear error when the symlink target is read-only
  • Add a unit test that asserts the symlink is preserved and the target is updated

Testing

  • nix develop -c cargo test -p codex-core

@github-actions
Copy link
Contributor

github-actions bot commented Jan 18, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@ryoppippi
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

@ryoppippi
Copy link
Author

recheck

github-actions bot added a commit that referenced this pull request Jan 18, 2026
@etraut-openai
Copy link
Collaborator

Thanks for the PR. Our contribution guidelines indicate that we're not currently accepting PRs for new features. The linked issue is a feature request, so I would normally close this PR. However, one could argue that this is a bug fix because Codex does follow symlinks for config.toml, so it should arguably handle updates if a symlink is present. This change is also pretty localized (doesn't affect UI or other user-visible behavior).

@etraut-openai
Copy link
Collaborator

I've reviewed your PR. I think it's directionally correct, but it has a lot of shortcomings:

  • It has hard-coded undocumented constants for symlink depth limits.
  • It doesn't detect symlink cycles or have a good fallback behavior when cycles are detected.
  • It doesn't handle all cases where config.toml is written.
  • It doesn't encapsulate the symlink-following logic into a utility routine.

I've prepared a separate PR that addresses these issues. I welcome you to review and comment on it.

I'm going to close this PR. Thanks again for taking the time to post it.

etraut-openai added a commit that referenced this pull request Jan 19, 2026
We already support reading from `config.toml` through a symlink, but the
code was not properly handling updates to a symlinked config file. This
PR generalizes safe symlink-chain resolution and atomic writes into
path_utils, updating all config write paths to use the shared logic
(including set_default_oss_provider, which previously didn't use the
common path), and adds tests for symlink chains and cycles.

This resolves #6646.

Notes:
* Symlink cycles or resolution failures replace the top-level symlink
with a real file.
* Shared config write path now handles symlinks consistently across
edits, defaults, and empty-user-layer creation.

This PR was inspired by #9437, which
was contributed by @ryoppippi
zeekay pushed a commit to hanzoai/dev that referenced this pull request Jan 24, 2026
We already support reading from `config.toml` through a symlink, but the
code was not properly handling updates to a symlinked config file. This
PR generalizes safe symlink-chain resolution and atomic writes into
path_utils, updating all config write paths to use the shared logic
(including set_default_oss_provider, which previously didn't use the
common path), and adds tests for symlink chains and cycles.

This resolves openai#6646.

Notes:
* Symlink cycles or resolution failures replace the top-level symlink
with a real file.
* Shared config write path now handles symlinks consistently across
edits, defaults, and empty-user-layer creation.

This PR was inspired by openai#9437, which
was contributed by @ryoppippi
lee101 pushed a commit to lee101/codex-infinity that referenced this pull request Jan 25, 2026
We already support reading from `config.toml` through a symlink, but the
code was not properly handling updates to a symlinked config file. This
PR generalizes safe symlink-chain resolution and atomic writes into
path_utils, updating all config write paths to use the shared logic
(including set_default_oss_provider, which previously didn't use the
common path), and adds tests for symlink chains and cycles.

This resolves openai#6646.

Notes:
* Symlink cycles or resolution failures replace the top-level symlink
with a real file.
* Shared config write path now handles symlinks consistently across
edits, defaults, and empty-user-layer creation.

This PR was inspired by openai#9437, which
was contributed by @ryoppippi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Honor config.toml symlinks when creating or updating config

2 participants