{"slug": "vs-code-codex-infinite-spinner-fix-reset-corrupted-webview-state", "title": "VS Code Codex infinite spinner fix: reset corrupted webview state", "summary": "A developer published a shell script to fix an infinite spinner bug in the VS Code Codex panel caused by corrupted webview state. The script resets Local Storage and Session Storage directories, which resolves a truncated JavaScript bundle error that prevented the chat UI from mounting. The fix was confirmed effective when Codex worked in a clean VS Code profile.", "body_md": "|\n#!/usr/bin/env bash |\n|\n# ===================================================================== |\n|\n# codex_fix.sh |\n|\n# |\n|\n# Diagnose and reset the VS Code webview state used by the Codex panel |\n|\n# from the openai.chatgpt extension. |\n|\n# |\n|\n# Symptom: |\n|\n# Codex works in the terminal, but the VS Code Codex panel stays on an |\n|\n# infinite spinner and the chat UI never appears. |\n|\n# |\n|\n# Confirmed root cause, 2026-07-04: |\n|\n# Corrupted VS Code webview state in: |\n|\n# |\n|\n# ~/.config/Code/Local Storage |\n|\n# ~/.config/Code/Session Storage |\n|\n# |\n|\n# The VS Code webview service worker served a truncated JavaScript bundle |\n|\n# for the Codex panel. The file on disk was valid, but the webview received |\n|\n# a broken version and failed with: |\n|\n# |\n|\n# Uncaught SyntaxError: Unexpected end of input |\n|\n# |\n|\n# Result: the Codex frontend did not mount, so the panel stayed on the |\n|\n# spinner. |\n|\n# |\n|\n# Important diagnostic: |\n|\n# Run VS Code with a clean profile: |\n|\n# |\n|\n# code --user-data-dir=/tmp/vscode-clean |\n|\n# |\n|\n# If Codex works there, the problem is not the Codex CLI, account, auth, |\n|\n# network, extension binary, or model list. It is local VS Code profile |\n|\n# state. |\n|\n# |\n|\n# What this script does: |\n|\n# - checks that VS Code is closed; |\n|\n# - finds the openai.chatgpt extension; |\n|\n# - checks webview JavaScript assets; |\n|\n# - deletes regenerable webview cache directories; |\n|\n# - moves Local Storage and Session Storage to .bak; |\n|\n# - provides a check mode after reopening VS Code. |\n|\n# |\n|\n# Usage: |\n|\n# 1. Close VS Code completely. |\n|\n# 2. Run: |\n|\n# |\n|\n# bash ~/codex_fix.sh |\n|\n# |\n|\n# 3. Open VS Code and open the Codex panel. |\n|\n# 4. Run: |\n|\n# |\n|\n# bash ~/codex_fix.sh check |\n|\n# |\n|\n# Notes: |\n|\n# - Cache directories are deleted because VS Code regenerates them. |\n|\n# - State directories are moved to .bak so rollback is possible. |\n|\n# ===================================================================== |\n|\n|\n|\nset -uo pipefail |\n|\n|\n|\nCFG=\"$HOME/.config/Code\" |\n|\nEXT=\"$(ls -dt \"$HOME\"/.vscode/extensions/openai.chatgpt-* 2>/dev/null | head -1)\" |\n|\n|\n|\n# Regenerable cache directories. These are removed. |\n|\nCACHE_DIRS=( |\n|\n\"Service Worker\" |\n|\n\"CachedData\" |\n|\n\"Code Cache\" |\n|\n\"GPUCache\" |\n|\n\"WebStorage\" |\n|\n) |\n|\n|\n|\n# Webview state directories. These are moved to .bak. |\n|\nSTATE_DIRS=( |\n|\n\"Local Storage\" |\n|\n\"Session Storage\" |\n|\n) |\n|\n|\n|\n# Asset name fragment from the observed webview console error. |\n|\nASSET_HINT=\"thread-side-panel-tabs\" |\n|\n|\n|\nprint_section() { |\n|\nprintf '\\n== %s ==\\n' \"$1\" |\n|\n} |\n|\n|\n|\ninfo() { |\n|\nprintf 'INFO: %s\\n' \"$1\" |\n|\n} |\n|\n|\n|\nok() { |\n|\nprintf 'OK: %s\\n' \"$1\" |\n|\n} |\n|\n|\n|\nwarn() { |\n|\nprintf 'WARN: %s\\n' \"$1\" |\n|\n} |\n|\n|\n|\nerror() { |\n|\nprintf 'ERROR: %s\\n' \"$1\" |\n|\n} |\n|\n|\n|\nnewest_codex_log() { |\n|\nlocal log_root |\n|\nlog_root=\"$(ls -dt \"$CFG\"/logs/*/ 2>/dev/null | head -1)\" |\n|\necho \"${log_root}window1/exthost/openai.chatgpt/Codex.log\" |\n|\n} |\n|\n|\n|\ncheck_mode() { |\n|\nprint_section \"CHECK: state after restarting VS Code\" |\n|\n|\n|\nif pgrep -x code >/dev/null; then |\n|\nok \"VS Code is running. This is expected in check mode.\" |\n|\nelse |\n|\nwarn \"VS Code is not running. Open VS Code and the Codex panel, then run check again.\" |\n|\nfi |\n|\n|\n|\nprint_section \"Latest Codex extension log\" |\n|\nlocal log_file |\n|\nlog_file=\"$(newest_codex_log)\" |\n|\n|\n|\nif [[ -f \"$log_file\" ]]; then |\n|\ninfo \"Log file: $log_file\" |\n|\necho \"-----\" |\n|\ntail -25 \"$log_file\" | sed 's/^/ /' |\n|\necho \"-----\" |\n|\n|\n|\nif grep -q \"Initialize received\" \"$log_file\"; then |\n|\nok \"The app-server initialized.\" |\n|\nelse |\n|\nwarn \"No initialization marker found in the latest log.\" |\n|\nfi |\n|\nelse |\n|\nerror \"Codex.log was not found. The extension may not have activated.\" |\n|\nfi |\n|\n|\n|\nprint_section \"App-server process\" |\n|\nif pgrep -af \"openai.chatgpt.*app-server\" >/dev/null; then |\n|\nok \"App-server process found:\" |\n|\npgrep -af \"openai.chatgpt.*app-server\" | sed 's/^/ /' |\n|\nelse |\n|\nwarn \"No app-server process found.\" |\n|\nfi |\n|\n|\n|\nprint_section \"Webview cache directories\" |\n|\nfor dir_name in \"${CACHE_DIRS[@]}\"; do |\n|\nif [[ -e \"$CFG/$dir_name\" ]]; then |\n|\ndu -sh \"$CFG/$dir_name\" 2>/dev/null | sed 's/^/ /' |\n|\nelse |\n|\necho \" missing: $dir_name\" |\n|\nfi |\n|\ndone |\n|\n|\n|\nprint_section \"Result interpretation\" |\n|\necho \"Logs only show the backend side. The backend may be healthy while the\" |\n|\necho \"webview frontend is still broken.\" |\n|\necho |\n|\necho \"To verify the actual panel:\" |\n|\necho \" 1. Open the Codex panel.\" |\n|\necho \" 2. Run: Ctrl+Shift+P\" |\n|\necho \" 3. Select: Developer: Open Webview Developer Tools\" |\n|\necho \" 4. Open the Console tab.\" |\n|\necho |\n|\necho \"Look for:\" |\n|\necho \" ${ASSET_HINT}-*.js Uncaught SyntaxError: Unexpected end of input\" |\n|\necho |\n|\necho \"If the error is gone and the chat is visible, the fix worked.\" |\n|\necho \"If the error remains, try installing another extension version:\" |\n|\necho \" Extensions -> Codex/OpenAI ChatGPT -> Gear -> Install Another Version\" |\n|\necho |\n|\necho \"Then consider disabling extension auto-update:\" |\n|\necho ' \"extensions.autoUpdate\": false' |\n|\n} |\n|\n|\n|\nmain_mode() { |\n|\nprint_section \"1. Is VS Code closed?\" |\n|\n|\n|\nif pgrep -x code >/dev/null; then |\n|\nerror \"VS Code is still running. PIDs: $(pgrep -x code | tr '\\n' ' ')\" |\n|\necho \"Cleaning webview state while VS Code is running is unreliable.\" |\n|\necho \"Close VS Code completely and run this script again.\" |\n|\nread -rp \"Continue anyway? [y/N] \" answer |\n|\ncase \"${answer:-}\" in |\n|\ny|Y|yes|YES) |\n|\nwarn \"Continuing while VS Code appears to be running.\" |\n|\n;; |\n|\n*) |\n|\necho \"Aborted.\" |\n|\nexit 1 |\n|\n;; |\n|\nesac |\n|\nelse |\n|\nok \"VS Code is closed.\" |\n|\nfi |\n|\n|\n|\nprint_section \"2. Codex extension on disk\" |\n|\n|\n|\nif [[ -z \"$EXT\" ]]; then |\n|\nerror \"openai.chatgpt extension was not found in ~/.vscode/extensions\" |\n|\nelse |\n|\nok \"Extension directory: $EXT\" |\n|\n|\n|\nif [[ -f \"$EXT/package.json\" ]]; then |\n|\nlocal version_line |\n|\nversion_line=\"$(grep -m1 '\"version\"' \"$EXT/package.json\" | tr -d ' \",')\" |\n|\ninfo \"$version_line\" |\n|\nelse |\n|\nwarn \"package.json was not found in the extension directory.\" |\n|\nfi |\n|\nfi |\n|\n|\n|\nprint_section \"3. Webview JavaScript asset check\" |\n|\n|\n|\nif [[ -n \"$EXT\" && -d \"$EXT/webview/assets\" ]]; then |\n|\nlocal suspicious |\n|\nlocal total |\n|\nsuspicious=0 |\n|\ntotal=0 |\n|\n|\n|\nfor file_path in \"$EXT\"/webview/assets/*.js; do |\n|\n[[ -e \"$file_path\" ]] || continue |\n|\ntotal=$((total + 1)) |\n|\n|\n|\nlocal tail_text |\n|\ntail_text=\"$(tail -c 40 \"$file_path\" | tr -d '\\n')\" |\n|\n|\n|\n# A healthy bundle usually ends with a JavaScript terminator or |\n|\n# sourceMappingURL. This is only a weak heuristic. |\n|\nif ! printf '%s' \"$tail_text\" | grep -qE '[};)]|sourceMappingURL'; then |\n|\nwarn \"Suspicious ending: $(basename \"$file_path\") ($(stat -c%s \"$file_path\") bytes)\" |\n|\necho \" tail: ${tail_text: -20}\" |\n|\nsuspicious=$((suspicious + 1)) |\n|\nfi |\n|\ndone |\n|\n|\n|\ninfo \"Checked JS files: $total\" |\n|\ninfo \"Suspicious JS files: $suspicious\" |\n|\n|\n|\nfor file_path in \"$EXT\"/webview/assets/${ASSET_HINT}-*.js; do |\n|\n[[ -e \"$file_path\" ]] || continue |\n|\n|\n|\nlocal size |\n|\nlocal parse_status |\n|\nsize=\"$(stat -c%s \"$file_path\")\" |\n|\n|\n|\nif command -v node >/dev/null 2>&1; then |\n|\nif node --input-type=module --check < \"$file_path\" >/dev/null 2>&1; then |\n|\nparse_status=\"parse OK\" |\n|\nelse |\n|\nparse_status=\"PARSE FAIL\" |\n|\nfi |\n|\nelse |\n|\nparse_status=\"node not found, parse check skipped\" |\n|\nfi |\n|\n|\n|\ninfo \"$(basename \"$file_path\"): ${size} bytes, ${parse_status}\" |\n|\ndone |\n|\nelse |\n|\nwarn \"Webview assets directory not found.\" |\n|\nfi |\n|\n|\n|\nprint_section \"4. Webview layers before cleanup\" |\n|\n|\n|\nfor dir_name in \"${CACHE_DIRS[@]}\" \"${STATE_DIRS[@]}\"; do |\n|\nif [[ -e \"$CFG/$dir_name\" ]]; then |\n|\ndu -sh \"$CFG/$dir_name\" 2>/dev/null | sed 's/^/ /' |\n|\nelse |\n|\necho \" missing: $dir_name\" |\n|\nfi |\n|\ndone |\n|\n|\n|\nprint_section \"5a. Deleting regenerable cache directories\" |\n|\n|\n|\nfor dir_name in \"${CACHE_DIRS[@]}\"; do |\n|\nif [[ -e \"$CFG/$dir_name\" ]]; then |\n|\nif rm -rf \"$CFG/$dir_name\"; then |\n|\nok \"Deleted: $dir_name\" |\n|\nelse |\n|\nerror \"Failed to delete: $dir_name\" |\n|\nfi |\n|\nelse |\n|\ninfo \"Already missing: $dir_name\" |\n|\nfi |\n|\ndone |\n|\n|\n|\nprint_section \"5b. Moving webview state directories to .bak\" |\n|\n|\n|\nfor dir_name in \"${STATE_DIRS[@]}\"; do |\n|\nif [[ -e \"$CFG/$dir_name\" ]]; then |\n|\nrm -rf \"$CFG/$dir_name.bak\" 2>/dev/null |\n|\n|\n|\nif mv \"$CFG/$dir_name\" \"$CFG/$dir_name.bak\"; then |\n|\nok \"Moved to .bak: $dir_name\" |\n|\nelse |\n|\nerror \"Failed to move: $dir_name\" |\n|\nfi |\n|\nelse |\n|\ninfo \"Already missing: $dir_name\" |\n|\nfi |\n|\ndone |\n|\n|\n|\nprint_section \"6. Webview layers after cleanup\" |\n|\n|\n|\nlocal clean |\n|\nclean=1 |\n|\n|\n|\nfor dir_name in \"${CACHE_DIRS[@]}\" \"${STATE_DIRS[@]}\"; do |\n|\nif [[ -e \"$CFG/$dir_name\" ]]; then |\n|\nerror \"Still exists: $dir_name\" |\n|\nclean=0 |\n|\nelse |\n|\nok \"Missing as expected: $dir_name\" |\n|\nfi |\n|\ndone |\n|\n|\n|\nprint_section \"DONE\" |\n|\n|\n|\nif [[ \"$clean\" -eq 1 ]]; then |\n|\nok \"All target layers were cleaned.\" |\n|\nelse |\n|\nwarn \"Some target layers still exist. VS Code may have kept files open.\" |\n|\nfi |\n|\n|\n|\necho |\n|\necho \"Next steps:\" |\n|\necho \" 1. Open VS Code.\" |\n|\necho \" 2. Open the Codex panel and wait 10 to 15 seconds.\" |\n|\necho \" 3. Run:\" |\n|\necho |\n|\necho \" bash ~/codex_fix.sh check\" |\n|\necho |\n|\necho \"If the panel works, you may remove backups:\" |\n|\necho |\n|\necho \" rm -rf ~/.config/Code/*.bak\" |\n|\necho |\n|\necho \"If the cleanup made things worse, restore state directories:\" |\n|\n|\n|\nfor dir_name in \"${STATE_DIRS[@]}\"; do |\n|\necho \" rm -rf ~/.config/Code/\\\"$dir_name\\\" && mv ~/.config/Code/\\\"$dir_name.bak\\\" ~/.config/Code/\\\"$dir_name\\\"\" |\n|\ndone |\n|\n} |\n|\n|\n|\ncase \"${1:-}\" in |\n|\ncheck) |\n|\ncheck_mode |\n|\n;; |\n|\n\"\") |\n|\nmain_mode |\n|\n;; |\n|\n*) |\n|\nerror \"Unknown argument: $1\" |\n|\necho \"Usage:\" |\n|\necho \" bash ~/codex_fix.sh\" |\n|\necho \" bash ~/codex_fix.sh check\" |\n|\nexit 2 |\n|\n;; |\n|\nesac |", "url": "https://wpnews.pro/news/vs-code-codex-infinite-spinner-fix-reset-corrupted-webview-state", "canonical_source": "https://gist.github.com/blurman-ai/8a782c711befe6fb6c4c94f73a90c98f", "published_at": "2026-07-04 11:01:24+00:00", "updated_at": "2026-07-15 17:34:46.180187+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["VS Code", "Codex", "OpenAI"], "alternates": {"html": "https://wpnews.pro/news/vs-code-codex-infinite-spinner-fix-reset-corrupted-webview-state", "markdown": "https://wpnews.pro/news/vs-code-codex-infinite-spinner-fix-reset-corrupted-webview-state.md", "text": "https://wpnews.pro/news/vs-code-codex-infinite-spinner-fix-reset-corrupted-webview-state.txt", "jsonld": "https://wpnews.pro/news/vs-code-codex-infinite-spinner-fix-reset-corrupted-webview-state.jsonld"}}