Repository navigation
macOS: New Chat from Local Project creates a global chat instead of a project chat (cloud projects unaffected) #39489
Description
What version of the Codex App are you using (From “About Codex” dialog)?
Version 26.814.41957
What subscription do you have?
Enterprise
What platform is your computer?
macOS (Tahoe 26.6.2)
What issue are you seeing?
Summary
New chats can no longer be created inside Local Projects in the macOS desktop app. Using the New Chat button on a local project creates a normal top-level chat instead of a project-scoped chat. The resulting chat has no access to the local project's context, knowledge sources, or agent skills.
The issue only affects Local Projects. Cloud-based projects on the same account behave correctly.
Project configuration
- Project type: Local Project
- Connected to an Obsidian vault (local folder)
- Project contains existing chats and scheduled automations
What steps can reproduce the bug?
Steps to reproduce
- Create (or open) a Local Project connected to a local folder (e.g. an Obsidian vault).
- Hover over the project in the sidebar.
- Click the New Chat button.
- Observe where the new conversation is created.
- Ask the new chat a question that requires access to the project's files or agent skills.
What is the expected behavior?
Expected result
- A new chat is created inside the Local Project.
- The chat inherits the project's context.
- The chat can search the connected Obsidian vault.
- The chat has access to the project's configured agent skills.
- The chat appears under the Local Project in the sidebar.
Actual result
- A normal top-level chat is created in the general chat list.
- The chat is not associated with the Local Project.
- The chat behaves as though the project does not exist.
- It cannot search the connected vault.
- It has no access to the project's agent skills.
- There is no way to move the chat into the Local Project afterwards.
Additional information
Additional observations
- Existing chats that were previously created inside the Local Project remain present and continue to function.
- Scheduled automations attached to the Local Project continue to run successfully, including reading from and writing to the connected Obsidian vault.
- This suggests the Local Project itself and its indexing are still functioning, but new interactive chats are not being bound to the project.
- Clicking the project name only expands/collapses the sidebar section; there is no project view that can be opened.
- Right-clicking the project shows expected options such as Settings and Pin, but there is no alternative way to create a project-scoped chat.
Comparison with Cloud Projects
Cloud Projects on the same account behave as expected:
- Hovering over a Cloud Project and clicking New Chat creates a project-scoped chat.
- The chat inherits the project's context.
- Chats can still be moved into Cloud Projects via drag-and-drop or the Move to Project action.
The issue appears to be specific to Local Projects in the macOS desktop application.
Additional troubleshooting performed
- Created a brand new Local Project using a separate test folder.
- Same behaviour.
- Verified that the issue reproduces across multiple Local Projects.
- Restarted the desktop app.
- No change.
- Attempted to drag a chat into the Local Project.
- Not possible.
- Checked the chat context menu.
- Move to Project is available for Cloud Projects but not for Local Projects.
- Verified that project automations continue to function.
- Confirms the Local Project itself is still operational.
Impact
This prevents using Local Projects interactively. Although automations continue to function, it is no longer possible to start a conversation that has access to the project's local knowledge sources or agent skills.
Notes
This appears to be a regression affecting the binding between newly created chats and Local Projects rather than an issue with the Local Project, indexing, or connected files themselves.
Activity
liulili-ui commentedon Aug 24, 2026
Windows reproduction confirmed.
Environment
26.818.5229.026.818.41509Reproduction
Actual result
The chat is created as a normal global/cloud ChatGPT conversation instead of a local project-scoped Codex task. The local thread catalog records the failed chat with:
source_kind = chatgptcwd = nullproject_id = nullconversation_origin = nullThe desktop log shows the creation route as:
client-new-thread -> local-chatgpt -> /work/conversationThere is no local
thread/startcall for the failed creation.Expected result
A chat created from the Local Project's own New Chat button should inherit that project and its local workspace.
Existing local project chats still work correctly, and the problem reproduces across multiple Local Projects. This started after updating Codex Desktop, so this appears to be the same project-binding regression reported here, also affecting Windows.
I can provide sanitized thread metadata or the relevant desktop log excerpt privately if maintainers need it.
zchengb commentedon Sep 14, 2026
macOS reproduction confirmed, with an additional Local Project picker symptom.
Environment
26.903.61454Reproduction
Actual result
Additional symptom
The Work in a project picker also does not list all Local Projects:
This makes it look like the Local Projects themselves still exist, but some project/session association or registration used by new Chat/Work entry points is not being recognized correctly.
No project names, local paths, files, account identifiers, or private content are included here.
zchengb commentedon Sep 14, 2026
Duplicate comment posted in error — please disregard. The reproduction details are in the immediately preceding comment.
zchengb commentedon Sep 14, 2026
Duplicate comment posted in error — please disregard. The reproduction details are in the earlier macOS reproduction comment.
bpuckett74 commentedon Sep 28, 2026
Additional macOS reproduction and follow-up (September 28, 2026 UTC).
Environment: ChatGPT/Codex desktop
26.924.22138(build11645), bundled app-server0.158.0-alpha.2.1.The user reports clicking New Chat from an existing saved Local Project in the sidebar. The resulting chat is a local Codex thread whose recorded working directory matches the project's primary folder, but the desktop
list_threadsreadback reportsprojectId: nulland places it in general Tasks. The saved project remains registered; 18 other active Codex chats in the same listing have its correct ID, including a subsequently created chat.The user also tested the documented
/projectselector: selected the same Local Project for a new chat and sent a test message. That second chat likewise has the correct working directory butprojectId: nullinlist_threads. Thus/projectis not a workaround in this environment.Read-only local-state inspection adds a narrower discrepancy: Desktop's persisted
thread-project-assignmentsmaps contain the correct saved project ID for both failed chats, with local host membership and neither marked projectless. Yet the desktop chat listing returns null for both. The app-serverthreads.project_idis null for these chats, but also for a chat that Desktop correctly lists under the project, so that column alone does not explain the difference. Athread/startfor the first failed chat completed without a logged error; available logs do not expose the project-binding input or identify the failing component.This points to inconsistent project membership resolution across desktop/app-server state. It does not prove a specific code path or update caused the regression. Project/thread IDs, the local path, and chat contents are omitted from this public comment.
Yourmomsfitnesstrainer commentedon Oct 1, 2026
Additional macOS reproduction on October 1, 2026, with a Git branch visibility symptom.
The user reports repeatedly starting tasks from inside an existing saved Local Project. For the current task, I checked the desktop's read-only project and thread listings and the repository's Git state:
Other Codex tasks with this same repository as their working directory do report the saved project's non-null ID, so the project registration is present. The current task can access the checkout and Git confirms that the branch exists, but the branch is not shown for this task in the desktop UI. The user reports that this mismatch recurs when starting tasks inside projects.
Expected: a task started from a saved Local Project retains that project's ID, appears in the project, and shows the active Git branch. Actual: this task has the correct working directory but
projectId: null, appears in general Tasks, and does not show the branch.I did not test whether restarting the app repairs this task. I am not asserting which component dropped the association. Project name, absolute path, project/thread IDs, branch name, and repository content are omitted from this public report.
Yourmomsfitnesstrainer commentedon Oct 1, 2026
Follow-up with a partial local workaround tested on macOS (October 1, 2026). I installed user-level Codex hooks that attach a missing backend Project ID to new local Codex tasks when their working directory matches a registered Local Project. This does not repair the desktop sidebar/cache: for the current task, the backend assignment is verified, while the live desktop
list_threadsresult still saysprojectId: nulland lists it under general Tasks. Please do not treat this as a fix for the UI or Git branch display.Hook wiring
I added command handlers to
~/.codex/hooks.json(merging them with existing handlers, not replacing them):{ "hooks": { "SessionStart": [{ "matcher": "^(startup|resume)$", "hooks": [{"type": "command", "command": "/usr/bin/python3 /ABSOLUTE/PATH/project-binding/launcher.py 2>/dev/null || true", "timeout": 15, "additionalContextLimit": 2000}] }], "UserPromptSubmit": [{ "hooks": [{"type": "command", "command": "/usr/bin/python3 /ABSOLUTE/PATH/project-binding/launcher.py 2>/dev/null || true", "timeout": 15, "additionalContextLimit": 2000}] }], "PreToolUse": [{ "hooks": [{"type": "command", "command": "/usr/bin/python3 /ABSOLUTE/PATH/project-binding/launcher.py 2>/dev/null || true", "timeout": 15}] }] } }The three events all call the same idempotent worker.
SessionStartcan fire before a new thread is persisted, soUserPromptSubmitandPreToolUseprovide bounded later chances. Review/trust the hooks through Codex's normal hook approval flow. The placeholder path must be replaced with an absolute path to a local script. See the official hooks documentation for event and output formats.What the worker does
Read the hook's
session_id(orCODEX_THREAD_ID) and validate it. Open the newest~/.codex/state_*.sqliteread-only. QuerySELECT * FROM threads WHERE id=?andSELECT p.id,p.name,r.path FROM projects p JOIN project_roots r ON p.id=r.project_id. Never write to SQLite or the desktop global-state JSON.Stop if the task predates installation, is a subagent, already has
threads.project_id, or has an explicit desktop assignment that cannot be matched safely. Resolve the task's recordedcwdagainst registered project roots; use the deepest unambiguous root. For linked Git worktrees, project the worktree path back to the main checkout's registered root. If there is no unique match, do nothing.Immediately before mutation, re-read the thread and project/desktop assignment. If the working directory, project catalog, or assignment changed, do nothing. For the matching task, start a short-lived
codex app-server --stdioprocess with hooks disabled in that child (-c features.hooks=false) to avoid recursion, and pointsqlite_homeat the same state directory. Sendinitializeandinitializedon its JSON-RPC stdin, then call the internal method:{"id": 2, "method": "thread/metadata/update", "params": {"threadId": "<thread-id>", "projectId": "<registered backend project id>"}}Require the RPC response to contain that Project ID, then verify it with a fresh read-only SQLite snapshot. Only after both checks does the hook emit model context saying which project was matched. It explicitly says the desktop UI is unverified. A per-thread journal and file lock prevent repeat writes. The launcher has a 12-second worker deadline, always exits successfully so normal work continues, and temporarily disables itself after three consecutive failures. A
DISABLEDmarker and removal of only these hook handlers provide a reversible off switch.My implementation also compares any desktop-side
thread-project-assignmentsin~/.codex/.codex-global-state.jsonread-only so it will not overwrite an explicit user choice. It stores only small status/error codes locally; no prompts, transcripts, tokens, or raw API errors. I backed up the prior hook config before installing and added an agent instruction to use the verified backend metadata as project context when desktop still reports null.This is a version-sensitive workaround, not a supported Project API: it depends on local SQLite table names and an internal app-server method. A different Codex release may need adaptation. The remaining discrepancy in this reproduction is precisely that a confirmed backend Project ID does not make the desktop's live project listing recognize this task.