Skip to content

Instantly share code, notes, and snippets.

@gzxu
Last active May 13, 2026 13:58
Show Gist options
  • Select an option

  • Save gzxu/68855015e606c972ebdbe1cab182fbde to your computer and use it in GitHub Desktop.

Select an option

Save gzxu/68855015e606c972ebdbe1cab182fbde to your computer and use it in GitHub Desktop.
Address "Error: Uncaught Error: !!! NLS MISSING" when starting VS Code in serve-web mode via the CLI

Background

See microsoft/vscode#299425 for additional context. The issue typically presents with the following symptoms:

  1. The page becomes blank after VS Code Server is downloaded and started.
  2. The browser developer console (F12) shows errors such as workbench.js:405 Uncaught Error: !!! NLS MISSING:.

Solution

  1. Open the browser developer tools (F12) and refresh the VS Code web page. You should see two requests for nls.messages.js. The second request should target the localized file, for example: https://www.vscode-unpkg.net/nls/560a9dba96f961efea7b1612916f89e5d5d4d679/1.116.0/zh-cn/nls.messages.js.
  2. Right-click the localized request and select Replace content. Chrome will open the Sources tab, which allows you to override the contents of the file. As noted by @connor4312 in this reply, the current content is broken.
  3. Run python3 merge-nls.py. The script generates ~/third_party/vscode/nls.messages.js.
    • If you are using a language other than Chinese, update the script accordingly.
  4. Copy the contents of the generated file into the override in Chrome's Sources tab.
  5. Refresh the page. VS Code should then load correctly in your language.

Note: This works only if you keep the developer tools open.

#!/usr/bin/env python3
import gzip
import io
import json
import zipfile
from pathlib import Path
from urllib.request import urlopen, Request
VSIX_URL = (
"https://marketplace.visualstudio.com/_apis/public/gallery/publishers/MS-CEINTL"
"/vsextensions/vscode-language-pack-zh-hans/latest/vspackage"
)
TRANSLATIONS_PATH_IN_VSIX = "extension/translations/main.i18n.json"
COMMIT = "0958016b2af9f09bb4257e0df4a95e2f90590f9f"
SERVE_WEB_DIR = Path.home() / ".vscode/cli/serve-web" / COMMIT / "out"
VSCODE_CDN_OUT_URL = f"https://main.vscode-cdn.net/stable/{COMMIT}/out"
NLS_KEYS_FILE = SERVE_WEB_DIR / "nls.keys.json"
NLS_MESSAGES_FILE = SERVE_WEB_DIR / "nls.messages.json"
NLS_KEYS_URL = f"{VSCODE_CDN_OUT_URL}/nls.keys.json"
NLS_MESSAGES_URL = f"{VSCODE_CDN_OUT_URL}/nls.messages.json"
OUTPUT_FILE = Path.home() / "third_party/vscode/nls.messages.js"
def download_url(url: str) -> bytes:
req = Request(url, headers={"Accept": "*/*"})
with urlopen(req) as resp:
response_bytes = resp.read()
if response_bytes.startswith(b"\x1f\x8b"):
return gzip.decompress(response_bytes)
return response_bytes
def download_json(url: str) -> object:
return json.loads(download_url(url).decode("utf-8"))
def download_translations() -> dict:
print("Downloading language pack from Marketplace...")
req = Request(VSIX_URL, headers={"Accept": "application/octet-stream"})
with urlopen(req) as resp:
vsix_bytes = resp.read()
print(f"Downloaded {len(vsix_bytes) / 1024 / 1024:.1f} MB")
vsix_bytes = gzip.decompress(vsix_bytes)
with zipfile.ZipFile(io.BytesIO(vsix_bytes)) as zf:
with zf.open(TRANSLATIONS_PATH_IN_VSIX) as f:
return json.load(f)
def main() -> None:
# 1. Download and extract translations from the VSIX
translations = download_translations()
contents: dict[str, dict[str, str]] = translations.get("contents", {})
# 2. Load build-time NLS metadata
nls_keys: list[list] = json.loads(NLS_KEYS_FILE.read_text("utf-8"))
nls_messages: list[str] = json.loads(NLS_MESSAGES_FILE.read_text("utf-8"))
# # 2. Download build-time NLS metadata
# print(f"Downloading VS Code NLS metadata for commit {COMMIT}...")
# nls_keys = download_json(NLS_KEYS_URL)
# if not isinstance(nls_keys, list):
# raise ValueError("nls.keys.json must be a JSON array")
# nls_messages = download_json(NLS_MESSAGES_URL)
# if not isinstance(nls_messages, list) or not all(
# isinstance(message, str) for message in nls_messages
# ):
# raise ValueError("nls.messages.json must be a JSON array of strings")
# 3. Merge: iterate nls.keys.json in order, look up each key in the
# language pack, fall back to the English message from nls.messages.json.
result: list[str] = []
nls_index = 0
translated_count = 0
for module_id, nls_module_keys in nls_keys:
module_translations = contents.get(module_id, {})
for nls_key in nls_module_keys:
translated = module_translations.get(nls_key)
if translated:
result.append(translated)
translated_count += 1
else:
result.append(nls_messages[nls_index])
nls_index += 1
assert nls_index == len(nls_messages), (
f"Index mismatch: iterated {nls_index} keys but nls.messages.json has {len(nls_messages)} entries"
)
# 4. Write merged output as a JS file that sets the NLS globals
messages_json = json.dumps(result, ensure_ascii=False)
js_content = (
f"globalThis._VSCODE_NLS_MESSAGES = {messages_json};\n"
f'globalThis._VSCODE_NLS_LANGUAGE = "zh-cn";\n'
)
OUTPUT_FILE.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_FILE.write_text(js_content, "utf-8")
print(f"Total messages: {len(result)}")
print(f"Translated: {translated_count}")
print(f"English fallback: {len(result) - translated_count}")
print(f"Written to: {OUTPUT_FILE}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment