Skip to content

About

Local compatibility proxy + Windows GUI for Codex/OpenAI-compatible Responses endpoints missing function_call_output.call_id

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Codex Call ID Fix

English | 简体中文 | 繁體中文

A local repair proxy + small Windows GUI for the Codex Desktop error:

Invalid 'call_id': call_id is required for function_call_output.

Codex sometimes seeds child-thread tool delegations (create_thread / send_message_to_thread) as function_call_output items without a call_id. Strict OpenAI-compatible Responses endpoints reject that. The proxy rewrites those synthetic items into ordinary user messages before forwarding the request, so the provider sees a valid payload.

It is intentionally provider-agnostic: any [model_providers.*] section in Codex's config.toml can be routed through it, and the listening/upstream proxy ports are configuration values, not constants.

Status: compatibility shim (0.1.6). The original failure was reproduced on 2026-11-11 with the original provider after quota was restored; enabling the fix proxy resolved it. This is a confirmed workaround in that environment, not a universal Codex fix; validate with rewrote= in backend/server.log for your provider/version.

This is an independent project, not affiliated with or endorsed by OpenAI.

Features

  • Repairs synthetic function_call_output items (no call_id) in Responses requests.
  • One local proxy can route any number of providers using /_p/<provider>/... paths.
  • Start/stop the proxy without hand-editing config.toml.
  • Switch Codex's active model provider from the GUI, including OpenAI (built-in) (removes the model_provider key).
  • Three egress modes:
    • proxy: through an upstream HTTP proxy (CONNECT).
    • direct: direct TCP/TLS to the provider.
    • auto: try the upstream proxy, fall back to direct.
  • Manages ~/.codex/.env together with the route, so Codex itself follows the same choice when the proxy is off.
  • Optional Windows logon autostart (hidden, no admin required).
  • CLI and Tkinter GUI.
  • Light, dark, and follow-system appearance (the GUI remembers your choice).
  • Simplified Chinese, Traditional Chinese, and English UI; separate Chinese and English font families plus font size.
  • Ports are editable in the GUI / callidfix.config.json.

Install (Windows)

Download and run:

CodexCallIdFix-Setup-0.1.6.exe

The installer is per-user (%LOCALAPPDATA%\Programs\CodexCallIdFix), needs no administrator rights, copies the bundled Node runtime, creates shortcuts, and runs an installation self-check. If the check passes, the installer deletes itself.

If you do not want an install, extract CodexCallIdFix-0.1.6-portable.zip and run CodexCallIdFix.exe inside it. The portable folder contains backend/, runtime/node.exe, the docs, and the signing helper. The bare CodexCallIdFix.exe alone is not a complete distribution: it needs a sibling/parent backend/ and a Node runtime (bundled or on PATH).

After installation:

  1. Open Codex Call ID Fix.
  2. Pick your provider in the Codex Model Provider box.
  3. Choose Codex route:
    • Fix proxy (local port) — Codex goes through this proxy.
    • Upstream proxy — Codex goes directly to the upstream HTTP proxy, proxy off.
    • Direct — Codex goes direct, proxy off.
  4. If using the fix proxy, choose Fix proxy egress: Upstream proxy / Direct / Auto fallback.
  5. Set the local listening port and upstream proxy host/port in the settings card. They are written to backend/callidfix.config.json.
  6. Completely quit and restart Codex Desktop after changing config.toml, .env, or the provider.

The proxy port defaults are examples (7891 local, 7890 upstream); change them to anything you like in the GUI.

Interface preferences live in the Menu → Settings… dialog (secondary menu), not on the main screen:

  • Language: Simplified Chinese, Traditional Chinese, English.
  • Appearance: Follow system / Light / Dark. Follow-system reads the Windows personalization setting and re-checks it every few seconds.
  • Separate Chinese font and English font family selection, plus font size. The Chinese UI uses the Chinese font; the English UI uses the English font.

The choices are saved in backend/gui.json (generated runtime file).

Backups and uninstall

Every installation/upgrade creates an automatic pre-install backup under:

%LOCALAPPDATA%\CodexCallIdFix\backups\<timestamp>-v0.1.6\
  manifest.json
  codex\config.toml        (if present at install time)
  codex\.env               (if present)
  install\backend\...      (existing config/state/gui on upgrade)

The actual backup root is also written to backup-root.txt in the install folder. This snapshot is for manual disaster recovery; it is not blindly copied back over config.toml, because that would discard changes you made after installation.

Portable builds have no timestamped setup backup. On the first config.toml write, manage.js creates a one-time immutable snapshot instead:

~/.codex/config.toml.callidfix-backup
~/.codex/config.toml.callidfix-backup.json

It is not overwritten on later provider/route changes. Delete it explicitly if you want a fresh snapshot.

Uninstall (uninstall.cmd, also used by Windows Apps & features when the registry entry was created) does this in order:

  1. manage.js off — restores tool-managed base_url values, restores/parks .env according to the current egress mode, and stops the proxy.
  2. manage.js autostart off — removes the logon autostart entry.
  3. Removes the project child certificate; keeps the CN=Hungry-M root while another project child certificate uses it.
  4. Removes the uninstall registry key, Run value, and Start Menu/Desktop shortcuts.
  5. Removes the install directory.
  6. Deletes the backup root only after step 1 succeeded.

Portable cleanup

The portable ZIP also includes cleanup.cmd / cleanup.ps1. It restores the tool-managed Codex config, disables autostart, removes the project certificate, removes matching Run/uninstall entries and shortcuts whose target is that folder, and optionally removes backups. It does not scan or delete unrelated Programs folders.

cleanup.cmd -Yes
cleanup.cmd -Yes -KeepBackups
cleanup.cmd -Yes -RemoveFolder
cleanup.cmd -Yes -RemoveRoot

-RemoveFolder schedules deletion of the portable folder after the script exits. Without it, the folder is left in place.

-RemoveRoot removes the CN=Hungry-M root and all certificates issued by it, including other projects. Use it only on a test machine or when you intentionally want to purge the local root. Without it, cleanup keeps the root while another project child certificate still exists.

If step 1 fails (for example a provider's original URL is unknown), uninstall shows a warning, keeps both the installation and the backup, and exits without deleting anything. Fix fallbackOriginalBaseUrls or the provider section, then run uninstall.cmd again.

Testing on another PC

The original failure was reproduced on 2026-11-11 with the original provider after quota was restored, and enabling the fix proxy resolved it. Versions are recorded in docs/environment.md; the checklist is in docs/reproduce.md. The 0.1.6 verification record is in docs/verification-0.1.6.md. Do not attach .codex auth/state or API keys to an issue.

Egress modes

Fix proxy egress only applies when Codex route = Fix proxy. It controls how the fix proxy reaches the provider:

  • Upstream proxy — the fix proxy connects to the configured HTTP proxy (proxyHost:proxyPort), sends CONNECT <provider>:443, then forwards the request. Use this when the provider is only reachable through that proxy.
  • Direct — the fix proxy opens a raw TCP/TLS connection to the provider and bypasses the upstream proxy and Windows system proxy.
  • Auto fallback — try the upstream proxy first (bounded by autoProbeMs, 3 seconds by default); if the TCP/CONNECT step fails, retry directly. If the upstream proxy accepts the connection but the provider returns an HTTP error, it does not retry direct.

Provider profiles (Desktop overlay)

Codex 0.134+ removed the legacy top-level profile = "<name>" selector and [profiles.<name>] tables. Profiles are now separate files:

~/.codex/<name>.config.toml

The CLI selects one with codex --profile <name>. Codex Desktop has no profile selector, so this tool reads <provider>.config.toml and overlays its managed keys (model, model_provider, model reasoning/verbosity, catalog, approval, approvals reviewer, review model, sandbox) into the top-level config.toml. It never writes the legacy profile key.

On the first switch it snapshots the current top-level settings into <current-provider>.config.toml (once; never overwritten). If the target has no profile, it clears model and model_catalog_json instead of carrying the previous provider's values, and prints a warning.

Example profile file (top-level keys, without [profiles.*]):

model_provider = "openai"
model = "gpt-5.6-sol"
model_catalog_json = "C:\\Users\\you\\.codex\\model-catalogs\\openai.json"

Provider transport settings stay in config.toml under [model_providers.<id>]; do not put model, catalog, approval, or sandbox keys there. The Desktop overlay is a compatibility bridge; for CLI profile mode, use codex --profile <name> directly.

How it works

Codex
  -> http://<listenHost>:<listenPort>/_p/<provider>/<original path>
  -> codex-callid-fix (rewrites body)
  -> upstream HTTP proxy  or  direct TCP/TLS
  -> the provider's original base_url

on rewrites the provider's base_url like this:

[model_providers.MyProvider]
base_url = "https://api.example.com/v1"

to:

[model_providers.MyProvider]
base_url = "http://127.0.0.1:7891/_p/MyProvider/v1"

The /_p/<provider>/ prefix lets one listening port serve several providers. off scans all [model_providers.*] sections and restores any base_url that points at this proxy.

Original URLs are maintained in two places:

  1. backend/state.json — originalBaseUrls (recorded by on);
  2. backend/callidfix.config.json — fallbackOriginalBaseUrls (automatically published by on, used if state.json is lost).

Only the base_url line is changed; comments, other keys, BOM, and line endings are preserved. If the original URL cannot be determined, off refuses to stop rather than leaving Codex pointed at a dead proxy.

Provider switching

The GUI lists:

  • OpenAI (built-in) — deletes the top-level model_provider key, letting Codex use its built-in provider.
  • Every [model_providers.<id>] found in ~/.codex/config.toml.

Selecting a custom provider writes model_provider = "<id>". It does not change model or model_catalog_json; if Codex reports an unknown model after switching, choose the matching model in Codex's model picker (or update model_catalog_json yourself). This is deliberate: model names are provider-specific and the tool should not guess.

The proxy manages all custom providers by default ("providers": "all"), so switching the active provider does not require re-applying the proxy. To manage only a subset, use an explicit array:

"providers": [
  "MyProvider",
  { "id": "AnotherProvider", "egress": "direct" }
]

Per-provider egress overrides the global mode while the proxy is on.

Codex .env

The route selector also controls ~/.codex/.env:

  • proxy / auto: ensure HTTP_PROXY, HTTPS_PROXY, ALL_PROXY point at the configured upstream proxy, and merge localhost,127.0.0.1,::1 into NO_PROXY.
  • direct: remove those proxy variables. If the file only contained proxy variables it is deleted; the content is saved to ~/.codex/.env.callidfix-parked and restored when switching back.
  • Other variables (API keys) are preserved.
  • The first edit creates ~/.codex/.env.callidfix-backup.

Changes to .env require a Codex Desktop restart.

If your proxy client also sets the Windows system proxy, deleting .env alone may not force Codex direct; disable the system proxy in that client. The fix proxy's own direct mode uses raw TCP/TLS and ignores system proxy settings.

Autostart

Enable Start automatically at Windows sign-in (hidden) in the GUI, or:

node backend/manage.js autostart on
node backend/manage.js autostart status
node backend/manage.js autostart off

This writes an HKCU\Software\Microsoft\Windows\CurrentVersion\Run value named codex-callid-fix. At logon, start-hidden.vbs runs manage.js boot, which reconciles the last on/off state: if the proxy was ON it starts it and repairs config.toml/.env; if it was OFF it stops stray processes and leaves the configuration direct. No administrator rights are needed.

CLI

The GUI is a front-end for the same CLI:

node manage.js on
node manage.js off
node manage.js toggle
node manage.js status [--json]
node manage.js restart
node manage.js route fix [proxy|direct|auto]
node manage.js route upstream
node manage.js route direct
node manage.js egress <proxy|direct|auto>
node manage.js provider list [--json]
node manage.js provider use <id|openai>
node manage.js autostart <on|off|status>
node manage.js boot

Examples:

# point all custom providers at the fix proxy, direct outbound
node backend/manage.js route fix direct

# stop the proxy; Codex uses the upstream proxy directly
node backend/manage.js route upstream

# stop the proxy; Codex goes direct
node backend/manage.js route direct

# switch Codex to the built-in OpenAI provider
node backend/manage.js provider use openai

Health check:

curl.exe http://127.0.0.1:7891/__callidfix/health

Configuration

backend/callidfix.config.json:

{
  "listenHost": "127.0.0.1",
  "listenPort": 7891,
  "providers": "all",
  "fallbackOriginalBaseUrls": {},
  "egress": {
    "mode": "proxy",
    "proxyHost": "127.0.0.1",
    "proxyPort": 7890,
    "connectTimeoutMs": 15000,
    "autoProbeMs": 3000
  },
  "codexEnv": {
    "manage": true,
    "noProxy": "localhost,127.0.0.1,::1"
  }
}
  • listenPort: local fix-proxy port. Change it in the GUI or here; on/restart rebinds and rewrites base_urls.
  • egress.proxyHost / proxyPort: upstream HTTP proxy. Use any port; nothing is hard-coded in the proxy logic.
  • egress.mode: proxy, direct, or auto.
  • providers: "all" or an explicit array.
  • fallbackOriginalBaseUrls: automatically populated by on; can also be edited manually for recovery.
  • codexEnv.manage: set false if you do not want the tool to touch ~/.codex/.env.

Build from source

Requirements: Windows 10/11, Python 3.10+, PyInstaller, sv-ttk, Node.js (for running the backend during development and for staging the runtime).

python -m pip install pyinstaller sv-ttk
node --test
powershell -NoProfile -ExecutionPolicy Bypass -File packaging/build.ps1

node --test runs the rewrite tests in tests/ without external dependencies.

Outputs:

dist/CodexCallIdFix.exe
dist/CodexCallIdFix-Setup-0.1.6.exe
dist/CodexCallIdFix-0.1.6-portable.zip
dist/SHA256SUMS.txt

Development run:

python gui/app.py
node backend/manage.js status

The installer embeds the GUI exe, backend/, a portable node.exe, the icon, README.md, README.zh.md, README.zh-Hant.md, LICENSE, and AUTHORS.md. The uninstaller is written to the install directory and registered under Windows Apps & features (per-user).

A GitHub Actions workflow (.github/workflows/build.yml) builds on every push and attaches the EXEs/checksums to a release when a v* tag is pushed.

Author and code signing

Author metadata lives in AUTHORS.md, the PyInstaller version resources, and the installer's Publisher value. The author is Hungry-M (https://github.com/Hungry-M). No email address is embedded in the binaries or package metadata; a GitHub noreply address is fine for commits and open-source metadata.

The release binaries are not signed by default. Unsigned builds work, but Windows SmartScreen shows "Unknown publisher". To sign:

  1. Obtain an Authenticode code-signing certificate (OV or EV). For public releases prefer a CA-issued certificate; a self-signed certificate is not trusted by other users. The CA requires a verifiable real identity/contact, so a noreply email is not enough for the certificate itself.
  2. Never commit the .pfx or its password. Use a secret store / CI secret.
  3. After packaging/build.ps1, run:
# PFX file
$env:CODESIGN_PFX = 'C:\secure\codesign.pfx'
$env:CODESIGN_PFX_PASSWORD = '...'
powershell -NoProfile -ExecutionPolicy Bypass -File packaging/sign.ps1

# or a certificate already imported into the user's certificate store
$env:CODESIGN_THUMBPRINT = 'ABCDEF...'
powershell -NoProfile -ExecutionPolicy Bypass -File packaging/sign.ps1

signtool.exe comes from the Windows SDK. CI can sign automatically by setting the CODESIGN_PFX_BASE64 and CODESIGN_PFX_PASSWORD repository secrets.

Local self-signing (this machine only)

The installer includes:

trust-sign.cmd
trust-sign.ps1

After installation you can run Start Menu → Trust & Sign Codex Call ID Fix (or the GUI menu Menu → Trust & sign locally…). It:

  1. creates (or reuses) a local root CA in Cert:\CurrentUser\My (CN=Hungry-M) and trusts it in CurrentUser\Root;
  2. creates a project child certificate signed by that root (CN=codex-callid-fix) and adds it to CurrentUser\TrustedPublisher;
  3. waits for the GUI to close, signs CodexCallIdFix.exe with the child using SHA-256, then relaunches it (even if signing failed; failures are logged to %LOCALAPPDATA%\CodexCallIdFix\trust-sign.log).

The root can be shared by other local projects; each project should get its own child certificate under the same root. -RemoveCert removes the project child first, then removes the root only if no other child certificates issued by it remain. The installer's uninstaller calls the same cleanup.

Root sharing is local-only. Installing app A does not make a separately downloaded app B trusted on another user's machine; A's installer would have to explicitly add the root to the user's Root store, which this project does not do and does not recommend. Use a CA-issued certificate for public distribution.

This makes the file signed by a locally trusted publisher, so File Properties → Digital Signatures should show the signer. It does not make other computers trust the file, and SmartScreen reputation may still show a warning for a self-signed or downloaded file. Only use it on a machine you control. Remove the local certificate later with:

trust-sign.cmd -RemoveCert

-RemoveCert removes the CN=codex-callid-fix child from My / TrustedPublisher, and removes the CN=Hungry-M root only when no other project child certificate remains. It uses certutil -user -delstore because PowerShell refuses to delete items from the current user's Root store directly.

License

The project is licensed under the MIT License; see LICENSE. Bundled/third-party components have their own terms; see THIRD_PARTY_NOTICES.md. Security reports should use GitHub private vulnerability reporting per SECURITY.md. Contributions use DCO sign-off plus a lightweight inbound relicense grant; see CONTRIBUTING.md.

Layout

backend/
  manage.js              CLI: config.toml/.env/provider/autostart management
  server.js              local repair proxy
  rewrite.js             synthetic call_id rewrite logic
  start-hidden.vbs       hidden logon entry
  callidfix.config.json  user configuration
gui/app.py               Tkinter GUI (sv-ttk theme)
tests/rewrite.test.js    Node rewrite tests (`node --test`)
packaging/
  installer.py           self-deleting per-user installer
  build.ps1              one-command release build
  sign.ps1               optional Authenticode signing
  trust-sign.ps1/.cmd    local self-signing helper (installed with the app)
  cleanup.ps1/.cmd       portable/install cleanup helper
assets/app.ico           application/installer icon
.github/workflows/       CI build + optional signing + release
AUTHORS.md               author metadata
LICENSE                  MIT
THIRD_PARTY_NOTICES.md   bundled dependency licenses
third_party/             full third-party license texts
SECURITY.md              vulnerability reporting
CODE_OF_CONDUCT.md       community behavior
CONTRIBUTING.md          development and test notes
CHANGELOG.md             release history
stop.cmd                 CLI shortcut for "off"
README.zh.md             Simplified Chinese documentation
README.zh-Hant.md        Traditional Chinese documentation

Generated at runtime in backend/: state.json, server.log, node-path.txt, gui.json (GUI appearance preference). The install root also contains backup-root.txt; pre-install snapshots live under %LOCALAPPDATA%\CodexCallIdFix\backups\.

Windows Defender false positives

The Windows installer is built with PyInstaller onefile and is currently unsigned. Defender sometimes reports a generic machine-learning detection such as Trojan:Win32/Wacatac.H!ml on this kind of packed executable. That can be a false positive, but do not assume it is safe:

  1. Download the complete file (not a .crdownload partial).
  2. Compare its SHA-256 with the release's SHA256SUMS.txt.
  3. If the hash matches, report the false positive to Microsoft: https://www.microsoft.com/en-us/wdsi/filesubmission (select the software-developer / incorrectly detected option, include the detection name, release URL, and hash).
  4. If you need to test locally, use a file-specific allowance only after the hash check, and remove the allowance afterwards. Do not disable real-time protection or add broad exclusions.

Self-signing does not fix Defender/SmartScreen reputation. For public distribution, use a CA-issued code-signing certificate and consider avoiding nested onefile packing (for example, an onedir app packaged by a conventional installer).

Troubleshooting

  • Codex still shows the old error — fully quit Codex Desktop (not just close the window) and start it again so it re-reads config.toml and .env.
  • Codex shows openai -- (missing section) — config.toml sets model_provider = "openai" but has no matching [model_providers.openai] section. Remove that line to use the built-in OpenAI provider, or add an explicit provider section. This is a provider-label/configuration issue; auth.json authentication is separate and may still be valid.
  • Upstream proxy unreachable — the status card shows [unreachable]; use Auto fallback or Direct, or fix the host/port.
  • Local port already in use — change the local port in the GUI; on stops the recorded old instance and rebinds.
  • off refuses to stop — a provider points at the proxy but its original URL is unknown. Add it to fallbackOriginalBaseUrls, then run off again.
  • OpenAI model error after switching — model/model_catalog_json still point at the previous provider. Pick an OpenAI model in Codex or edit those keys.
  • EPERM ... config.toml...tmp — the process is running under a restricted token (for example Codex's Windows sandbox), which has read-only access to ~/.codex. Start the GUI from the Start Menu or Explorer instead of a Codex terminal. The same applies to manage.js provider use.
  • Run outside Codex's sandbox — writing ~/.codex from a restricted Codex terminal may be denied; use the GUI or a normal PowerShell.
  • Installer warns about shortcuts / uninstall registry — some hardened environments deny shortcut or registry writes to unsigned EXEs. The app still installs and self-checks; run uninstall.cmd from the install folder to remove it, and use the Start Menu shortcut if the Desktop one was skipped.
  • "backend/manage.js not found" — a bare CodexCallIdFix.exe cannot run without the backend. Use the installer or extract CodexCallIdFix-0.1.6-portable.zip; if running from a source checkout, keep backend/ next to (or one level above) the EXE and provide Node via runtime/node.exe or PATH.
  • "Could not create temporary directory" — the onefile launcher extracts to the user's temp directory, not next to the EXE. Make sure %TEMP% is writable and not redirected to a read-only location.
  • NTE_PERM / 0x80090010 when self-signing — the app is running under Codex's sandbox token, which has read-only access to %APPDATA%\Microsoft\Crypto\Keys; Windows cannot create the certificate private key. Launch the installed app from the Start Menu or Explorer, not from a Codex/DSH terminal, then retry. Do not grant the sandbox group write access to the crypto store.
  • SmartScreen says "Unknown publisher" — the release binaries are unsigned unless you sign them. See "Author and code signing" above, or use the local Trust & Sign helper for this machine.
  • Windows Apps entry missing — use uninstall.cmd in the install folder; it also calls manage.js off and autostart off before deleting the files.
  • Logs: backend/server.log (rotated at 5 MB).

Scope / disclaimer

This is a local workaround for a Codex Desktop serialization bug, not an OpenAI/Codex official component. It only listens on loopback by default and does not send your prompts anywhere except the provider base_url you already configured.

About

Local compatibility proxy + Windows GUI for Codex/OpenAI-compatible Responses endpoints missing function_call_output.call_id

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages