TuyaOpen IDE Preview — Bug Submissions

TuyaOpen IDE Preview — Bug Submissions

2026-08-23T06:40:00Z

Prepared for the TuyaOpen IDE Preview Tester Program (Bug Submission & Feedback phase).

Two parts:

  • Part A — 7 device/platform-level {bugs-cum-suggestion} found while building a real product end-to-end.
  • Part B — 14 source-level bugs found by auditing the SDK tooling itself
    (tools/cli_command/*.py, export.*, IDE-generated wrappers). Each has exact
    file:line references; B11 is reproduced live on this machine with before/after proof.

Global environment (applies to every report below)

Expand
Field Value
OS Windows 11 Pro, PowerShell 5.1 (also reproduced in cmd.exe)
IDE VS Code + Antigravity IDE with TuyaOpen IDE Preview extension v0.1.5
SDK TuyaOpen SDK main branch (IDE-managed install)
Toolchain Python 3.12.13 (SDK-managed), GNU Make 4.4.1
Hardware Tuya T5AI-Core development board (Beken BK7258, Wi-Fi + audio AI)
Test project Voice-first AI study assistant (base framework): AI audio chat, desk double-knock wake, LED indicator, app-controlled focus timer
Cloud side Tuya Platform product PID okqfzw6tkrabylcs, Lume AI Agent (Study Assist) project bound to the product, Ray panel v1.0.0

Labels used: preview-tester bug (add ux / feature-request where noted)


BUG A1 — Windows build fails at packaging: 'charmap' codec can't encode character

Labels: preview-tester bug Severity: High (blocks all CLI/scripted builds)

Environment

Windows 11 Pro · TuyaOpen IDE Preview v0.1.5 · SDK main · Python 3.12.13 · T5AI-Core.

Reproduction steps

  1. Create a TuyaOpen project (any framework, any board).
  2. Open a plain PowerShell terminal (not the IDE-integrated terminal).
  3. cd source/<project> and run python <SDK_ROOT>\tos.py build.

Expected behavior

Build compiles and packages the firmware image.

Actual behavior

Build fails during the packaging step:

UnicodeEncodeError: 'charmap' codec can't encode character ...

Python defaults to the legacy cp1252 codepage on Windows unless explicitly told otherwise.
The IDE-integrated terminal happens to work, so the failure only appears when building
from scripts, CI shells, or a fresh terminal.

Evidence

Terminal capture of failed build, then successful build after workaround (screenshots attached to forum post).

Workaround found

$env:PYTHONUTF8 = "1"
$env:PYTHONIOENCODING = "utf-8"

must be set before every external build.

Suggested fix

Set UTF-8 mode inside tos.py itself (e.g. re-exec with -X utf8) or document the
requirement prominently. Best: make packaging codepage-independent.


BUG A2 | SUGGESTION — T5AI-Core has no auto-reset circuit: flash tools hang waiting for the device

CASE(Only when): The Boot time is highly reduced either due to very good network connection or less complex firmware logic.
-Causing The Board to boot , connect instantly -leaving user no timw windows to debug again. [only case left is to hard reset.]

Labels: preview-tester bug ux Severity: Medium (first-run blocker)

Reproduction steps

  1. Connect T5AI-Core over USB.
  2. Start flashing from tyutool GUI (or tyutool_cli --plain write -d t5 -p COMx -f app.bin).
  3. Do not touch the board.

Expected behavior

Tool enters the bootloader automatically (as ESP32-class boards do via DTR/RTS auto-reset).

Actual behavior

Tool sits in handshake until you press the board RST button during the handshake window
(or power-cycle at the right moment). Nothing on screen explains this — first-time users
conclude flashing is broken.

Verified working recipe (for docs)

tyutool GUI, correct port @ 460800, click flash → press RST immediately
Verification passed. Flash succeeded. in ~1m31s.

Suggested fix

Show an explicit prompt/countdown in GUI+CLI (“Press RST now…”), detect the missed
handshake window, and document the manual-reset requirement per board.


BUG A3| SUGGESTION — USB serial port roles swap between sessions (log port ↔ flash port)

Labels: preview-tester bug ux Severity: Medium

Reproduction steps

  1. Note which COM port is the log console and which is the flash port
    (USB-Enhanced-SERIAL-A vs -B).
  2. Reboot the PC or replug the board several times.

Expected behavior

A stable, documented mapping between physical USB roles and COM ports.

Actual behavior

The roles swap unpredictably between sessions (observed both A=log/B=flash and the reverse).
Flashing to the “log” port times out; monitoring the “flash” port shows nothing.

Suggested fix

Label ports by role in tooling (read USB interface descriptor rather than enumeration order)
and show friendly names (“Flash”, “Log”) in GUI dropdowns.


BUG A4 — Device boots in HOLD conversation mode despite firmware defaulting to FREE

Labels: preview-tester bug Severity: Medium

Environment

Firmware uses AI audio components; conversation-mode DP 9 (hold/key/weakup/free) persisted via KV storage.

Reproduction steps

  1. Fresh flash; never touch conversation-mode DP.
  2. Boot device; try wake-word / hands-free interaction.
  3. Check KV / serial log.

Expected behavior

Device boots with the firmware-declared default (free), or clearly documents boot behavior.

Actual behavior

KV stores {"volume":86,"chat_mode":0} → mode 0 = HOLD, overriding the code default.
Hands-free doesn’t work until the user opens the app and sets DP 9 = free. Nothing tells
the user why the device “isn’t listening”.

Suggested fix

First-boot initialization should write the firmware default into KV (or distinguish
“never set” from “explicitly set”), and the panel should highlight non-free modes.


BUG A5 | SUGGESTION — Focus-timer start plays the Chinese power-on greeting instead of a dedicated chime

Labels: preview-tester bug ux Severity: Low

Reproduction steps

  1. Set the study focus timer (value DP) from the panel.
  2. Listen to the start feedback.

Expected behavior

A short neutral “timer started” tone/alert.

Actual behavior

The firmware reuses AI_AUDIO_ALERT_POWER_ON, which plays the localized power-on greeting
(Chinese TTS: “我在这里,我们一起玩吧”-style line). Users think the device rebooted or misheard.

Suggested fix

Provide a dedicated TIMER_START / TIMER_END entry in the alert enum (current enum:
POWER_ON, NOT_ACTIVE, NETWORK_CFG, NETWORK_CONNECTED, NETWORK_FAIL, NETWORK_DISCONNECT,
BATTERY_LOW, PLEASE_AGAIN, LONG_KEY_TALK, KEY_TALK, WAKEUP_TALK, RANDOM_TALK, WAKEUP).


BUG A6 | SUGGESTION— AI agent hallucinates device state (claims mute/LED/volume/timer control without ground truth)

Labels: preview-tester bug feature-request Severity: Medium (trust issue)

Reproduction steps

  1. Ask the agent: “mute the device” / “turn on the LED” / “how much time is left?”.
  2. Compare answer with actual device/panel state.

Expected behavior

Agent either performs the action via a registered tool/DP or honestly says it can’t.

Actual behavior

Without device-side state injection, the agent invents plausible-sounding answers
(“I’ve muted it for you”) while nothing changed. Only after we injected strict prompt rules
(“never claim device state; say ‘check the app’”) did responses become honest.

Suggested fix

Platform-level grounding: expose current DP snapshot to the agent context automatically,
or provide a standard “device state” tool-calling hook so agents stop hallucinating.


BUG A7 — Transient MQTT Cmd Parse Fail:-25344 during agent chat

Labels: preview-tester bug Severity: Low (recovers, but noisy)

Reproduction steps

  1. Use AI voice chat repeatedly over several minutes.
  2. Watch serial/console logs.

Expected behavior

Clean message flow, errors only on real faults.

Actual behavior

Intermittent Cmd Parse Fail:-25344 entries mid-session; session recovers on its own.
No user-visible effect observed, but the error code isn’t documented anywhere, making triage hard.

Suggested fix

Document error codes (-25344 etc.) in the SDK, and downgrade known-transient parse events to debug level.



PART B — Source-level bugs (SDK tooling audit)

All findings below come from reading the shipped tooling source in the installed SDK
(tools/cli_command/*.py, export.ps1/bat/sh, IDE-generated wrappers under
.tuyaopen/ide/bin/). Line numbers refer to the current main install.

B1 — Unquoted paths & user strings interpolated into shell=True commands: builds break on paths with spaces; project name is a command-injection surface

Labels: preview-tester bug Severity: High

Locations

  • tools/cli_command/cli_build.py:188cmd = f"cmake -G Ninja {open_root} " — SDK root unquoted.
  • tools/cli_command/cli_build.py:218-227-DTOS_PROJECT_ROOT={app_root} and all other defines unquoted, then executed via do_subprocess()subprocess.call(cmd, shell=True) (util.py:421).
  • tools/cli_command/cli_build.py:137,168-170{chip}, {project_name} {platform} {framework} {chip} passed unquoted to build_setup.py / platform_prepare.py.
  • tools/cli_command/cli_new.py:309python {porting_script} {new_platform_path} {new_platform_name} unquoted.
  • Irony: do_subprocess()'s own docstring (util.py:399-406) says it was rewritten so that “paths containing spaces stop breaking” — only cwd= got fixed; every interpolated argument is still raw.

Impact

  1. Space paths: SDK installed under e.g. C:\Users\John Doe\TuyaOpenSDK or a project dir like My Projects\app → cmake receives split args → configure fails with cryptic errors. Non-ASCII usernames (very common outside the US) hit this too.
  2. Injection: CONFIG_PROJECT_NAME comes from user-editable Kconfig. A name like x&calc or x" & calc &" is spliced into a cmd.exe /c command line. It’s the developer’s own machine, but any tool/agent that writes project names into configs (the IDE’s AI generation does!) turns this into arbitrary command execution during build.

Reproduction

Set CONFIG_PROJECT_NAME="a b" (or install SDK under a path with a space) → tos.py build.

Suggested fix

Use subprocess.run(list_args, shell=False) everywhere (the codebase already does list-form correctly in _tool_runs_ok, cli_prepare.py:244). At minimum, quote every interpolation.


B2 | FIXATON — GNU Make downloader: no checksum AND a poisoned archive cache permanently bricks builds

Labels: preview-tester bug Severity: High

Location

tools/cli_command/cli_prepare.py:139-227 (ensure_windows_make).

Bug 2a — No integrity check on downloaded make

The archive is fetched from a hardcoded CDN GUID URL (images.tuyacn.com/rms-static/f7061a90-….zip) and extracted with no SHA-256 verification — inconsistent with util_tyutool.download_tyutool_bin(), which does verify sha256. Whatever bytes the CDN returns get executed as make.exe.

Bug 2b — Corrupt cache = permanent failure loop (reproduced by code path)

if not os.path.isfile(archive_path):        # cli_prepare.py:177
    if not _download_file(...): return False # partial download → file EXISTS on disk
...
with zipfile.ZipFile(archive_path, "r") ...  # next run: "Reusing archive" → BadZipFile

_download_file() (util_tyutool.py:142-186) streams straight to the final path; a timeout
(DOWNLOAD_TIMEOUT, flaky Wi-Fi, VPN drop) leaves a truncated zip cached forever. Every later
build then crashes with an unhandled zipfile.BadZipFile traceback — there’s no except
clause around extraction — and self-healing never happens because the file-exists check skips
redownload. User must manually find and delete .tools/archives/make/4.4.1/…zip.

Suggested fix

Pin + verify sha256 (like tyutool), download to .tmp then atomic-rename, and treat any
extraction failure by deleting the cached archive.


B3 — Build temporarily rewrites the user’s GLOBAL git config (and a crash leaves China mirrors permanently)

Labels: preview-tester bug Severity: Medium-High

Location

tools/cli_command/util_git.py:34-67 (set_repo_mirro) called from cli_build.py:101-112.

Behavior

When timezone==UTC+8, before cloning a platform repo the tool runs
git config --global url."https://gitee.com/tuya-open/X".insteadOf https://github.com/tuya/X
for 13 repos, then unsets them in a finally: block.

Problems

  1. Global side effect: while any build runs, the user’s other terminals silently redirect clones of those GitHub repos to gitee. Nothing tells the user their ~/.gitconfig is being rewritten.
  2. Dirty state on hard kill: finally covers exceptions, but a killed process / IDE restart / power loss mid-clone leaves all 13 insteadOf entries permanently. Weeks later, unrelated clones of tuya/TuyaOpen-T5AI still go to gitee and the user has no idea why.
  3. Parallel builds race: two shells building simultaneously → one unsets while the other still needs it.

Suggested fix

Never touch global config: pass mirror URLs directly to git_clone() (it already has resolve_effective_git_url()), or use GIT_CONFIG_COUNT/GIT_CONFIG_KEY_n env vars scoped to the child process only.


B4 — tos.py flash without -p targets an arbitrary serial port (this is why flashing “hangs”)

Labels: preview-tester bug ux Severity: Medium

Location

tools/cli_command/cli_flash.py:309if port: → with no -p, the port flag is simply omitted and tyutool picks its default (first enumerated COM port). On T5AI-Core there are TWO USB-CDC ports per board (console vs flash), so auto-pick frequently selects the log console, where flashing hangs forever.

Compare cli_monitor.py:_choose_port() (lines 170-191): monitor does prompt when multiple ports exist — but prints only bare device names:

print(f"{i+1}. {p}")     # p.device only ("COM3"), no description!

serial.tools.list_ports provides .description (e.g. “USB-Enhanced-SERIAL-B (CH34x)”) which would instantly disambiguate console vs flash.

Suggested fix

Flash should reuse a port chooser that shows descriptions (and mark which ports look like flash-capable interfaces); monitor should print .description. One-line fix each.


B5 — Official entry points (export.bat/ps1/sh) don’t set UTF-8, so Bug A1 hits every non-IDE shell

Labels: preview-tester bug Severity: Medium (upgrades A1 from “workaround needed” to “fix belongs here”)

Grepped all three export scripts: no PYTHONUTF8, no PYTHONIOENCODING, no chcp.
export.* is documented as the mandatory entry point, yet the packaging step still crashes on
default cp1252 Windows shells. Two lines in export.* would kill the entire bug class:

$env:PYTHONUTF8 = "1"; $env:PYTHONIOENCODING = "utf-8"

(Or launch tos.py via python -X utf8.)


B6 — Asymmetric encoding in env store: read utf-8, write cp1252

Labels: preview-tester bug Severity: Low-Medium

tools/cli_command/util.py:347env_write() opens .cache/.env.json with
open(env_json, 'w') (no encoding) while env_read() (line 323) uses encoding='utf-8'.
Any stored value containing a non-cp1252 character (Chinese path segment, emoji in a
project field…) raises UnicodeEncodeError mid-write — and can leave a half-written JSON,
which then makes every subsequent env_read fail with “Read env json error” (caught, but
silently resets state). Fix: open(env_json, 'w', encoding='utf-8') + write to temp + rename.


B7 — Timezone UTC+8 ⇒ “China”: Singapore/Malaysia/W.Australia silently routed to CN infrastructure

Labels: preview-tester ux Severity: Medium (for international users)

tools/cli_command/util.py:260-285: country detection = “is local UTC offset exactly +8h”.
UTC+8 covers Singapore, Malaysia, Philippines, Western Australia, Irkutsk, parts of Siberia…
All of these users get gitee mirrors (set_repo_mirro), aliyun PyPI index
(cli_prepare.py:285-287), and tuyacn CDN downloads — which are slow or blocked for them.
No override flag exists (only undocumented OPEN_COUNTRY_CODE). Add tos.py config region
or respect OPEN_COUNTRY_CODE officially + document it.


B8 — IDE-generated CLI wrappers hardcode the extension’s versioned path; updates orphan every existing project

Labels: preview-tester bug ux Severity: Medium

.tuyaopen/ide/bin/tuyaopen.cmd contains:

node "c:\Users\<user>\.antigravity-ide\extensions\tuyaopen.tuyaopenide-0.1.5\out\cli\cli.js" %*

and devplat-cli-launcher.js:77 pins tuyaopen.tuyaopenide-0.1.5\vendor\.... After the
extension updates to 0.1.6, old projects’ wrappers point to a deleted directory → cryptic
Cannot find module from node until the IDE happens to regenerate them. Consider resolving
via a stable shim path (latest-version symlink/dir) or regenerating wrappers on extension
activation for all known projects.


B9 — Windows ARM64 silently gets x86_64 binaries

Labels: preview-tester feature-request Severity: Low

util_tyutool.get_platform_key() lines 43-45: on Windows the arch is forced to x86_64
regardless of platform.machine(). Snapdragon X / ARM64 laptops run tyutool under emulation;
works today, but native arm64 builds would avoid weird perf/AV issues, and the forced override
hides the mismatch from bug reports.


B10 — compile_commands.json (9 MB+) copied into the SDK root

Labels: preview-tester ux Severity: Low

cli_build.py:251-276 copies compile_commands.json to params["open_root"] (the SDK
root), not the app root — a ~9 MB artifact lands in the SDK install dir after every build.
It happens to be in the SDK .gitignore, but it still bloats backups/AV scans and belongs
next to the app (that’s where clangd needs it).


B11 — :star: tos.py build silently uses a STALE config: hand-editing app_default.config is ignored — LIVE REPRODUCED

Labels: preview-tester bug Severity: High (silent wrong-firmware builds)

Location

tools/cli_command/cli_config.py:99-101:

if force or not os.path.exists(using_config):
    _defconfig(app_default_config, using_config, catalog_kconfig)

using.config is regenerated only when missing. _invalidate_derived_artifacts()
(the only thing that clears derived state) is called from exactly ONE place:
config set (cli_config.py:414). Plain tos.py build never compares mtimes
(zero hits for getmtime/st_mtime in the whole codebase).

Live reproduction (performed on this machine, sandbox project)

  1. Fresh project with CONFIG_PROJECT_VERSION="0.1.0" → run build →
    .build/cache/using.config created with 0.1.0.
  2. Edit app_default.configCONFIG_PROJECT_VERSION="0.2.0".
  3. Run tos.py build again.

Result: app_default.config says 0.2.0; using.config STILL says 0.1.0 — and the
entire build pipeline runs with the stale values. Same applies to product ID, board symbols,
component enables… everything. The user “changes config, rebuilds” and flashes firmware that
silently doesn’t contain their change. Only workarounds: tos.py clean (full rebuild) or
re-doing changes via tos.py config set.

Suggested fix

In init_using_config, regenerate when mtime(app_default.config) > mtime(using.config)
(or hash-compare), and call _invalidate_derived_artifacts() when it changes.


B12 — :star: tos.py dev bac permanently overwrites the project’s app_default.config

Labels: preview-tester bug Severity: High (data loss)

Location

tools/cli_command/cli_dev.py:116-152 (build_all_config_exec):

for idx, config in enumerate(config_list):
    copy_file(config, app_default_config)   # overwrites YOUR app config
    ok = build_project(...)
...
# loop ends — original app_default.config is NEVER restored

dev bac iterates board/app configs and copies each one over app_default.config to drive
the build. After the last iteration, the developer’s hand-tuned default (product ID, board,
feature flags) has been replaced by whichever config sorted last. No backup, no restore, no warning.

Impact

Running the documented multi-config build once silently rewrites project identity
(CONFIG_TUYA_PRODUCT_ID etc.). Next normal build flashes firmware bound to the WRONG product.

Suggested fix

Save the original to memory/temp at start and restore in a finally: block (or pass the
config via -DTOS_* overrides instead of mutating files).


B13 — Systemic crash on non-interactive stdin: 6 unguarded input() calls raise raw EOFError

Labels: preview-tester bug Severity: Medium (breaks CI / AI-agent usage)

Only two call sites guard with sys.stdin.isatty() (util_tyutool.py:285,
cli_config.py:588). Everywhere else, any prompt crashes with an unhandled traceback as soon
as stdin is closed/piped — exactly how CI runners and coding agents invoke CLIs:

Location Prompt Crash trigger
cli_build.py:65 platform commit mismatch y/n/d build with outdated platform + no TTY
cli_monitor.py:185 serial port chooser monitor with multiple ports + no TTY
cli_new.py:424,432,475,540 new project/board/platform names scripted project creation
util.py:243 (list_menu) all interactive menus piped input

Example failure: tos.py check && tos.py build in CI where the platform repo is one commit
behind → EOFError traceback instead of a clear “platform update required, run X”.

Suggested fix

Central helper ask_choice() that returns a sensible default / fails with a readable error
when not sys.stdin.isatty() (or an explicit --yes/-n flag per command).


B14 — Running tos.py with a non-venv Python dies with a raw ModuleNotFoundError

Labels: preview-tester ux Severity: Low-Medium (first-contact onboarding)

python C:\...\TuyaOpenSDK\tos.py version with system Python (very common first attempt,
since docs say python tos.py …) crashes with:

ModuleNotFoundError: No module named 'git'

No hint that the SDK venv must be activated (export.ps1) or that .venv\Scripts\python.exe
must be used. A one-line try/except around the imports suggesting “run export.ps1 first”
would save every newcomer 20 confused minutes.


Task completion summary (Tasks 1–5)

Task Status Notes
1 — Installation :white_check_mark: Done Preview extension installed in VS Code + Antigravity IDE (v2.5.5)
2 — Project creation :white_check_mark: Done Natural-language project generation used; scaffolded base framework project on t5ai
3 — Flashing & HIL validation :white_check_mark: Done extensively tyutool GUI + CLI flows; found Bugs 3/4/5; full HIL loop verified (voice chat, LED, timer, panel control)
4 — AI Agent integration :white_check_mark: Done Claude Code wired through repo skill files (AGENTS.md/CLAUDE.md); multiple build→flash→monitor→fix loops completed; found Bugs 2/8
5 — Free exploration :white_check_mark: Done Built a complete product: desk double-knock wake detection on raw mic PCM, focus-timer with agent-context injection, Ray panel with style linkages; destructive tests included forced mute-during-knock, reflash-unbind, cloud-timer abuse

UX / feature-request suggestions (label: feature-request ux)

  1. Flash-time RST coach-mark (ties to Bug 4) — per-board flashing hints.
  2. PYTHONUTF8 default (Bug 1) — or ship sitecustomize.py in the SDK venv.
  3. Port role naming (Bug 5) — “Flash”/“Log” labels everywhere ports appear.
  4. Cloud timer DP validation (Bug 2) — platform-side schema check at publish.
  5. Unbind warning (Bug 3) — pre-flash warning + post-flash re-pair wizard.
  6. Agent device-state grounding (Bug 8) — standard DP-snapshot injection for agents.
  7. Timer alerts in alert enum (Bug 7) — TIMER_START / TIMER_END.
  8. Error-code dictionary (Bug 9) — searchable table of SDK/MQTT error codes in docs.

Submission checklist (per guidelines)

  • Environment block (OS / IDE / Preview version) — top of this file
  • Hardware model — T5AI-Core
  • Reproduction steps — every bug
  • Expected vs actual — every bug
  • Evidence — attach screenshots/logs listed per bug when posting
  • Severity — assigned per bug
  • Labels — preview-tester bug (+ ux/feature-request where relevant)
1 Like

For The People

This Report is fully logged while Developing project “Lume Agent” .(ˢᶜʳᵉᵉⁿˢʰᵒᵗ ᵃᵗᵗᵃᶜʰᵉᵈ)

The Report is verified as i had to debug those errors while discussing with LLM, throughout process.

So ; These improvements will leave TuyaOpen IDE to become flawless platform for AI Assisted Firmware Development.

Github: Lume Study AI Assistant

@naman That’s quite a lot. Thanks for the feedback! :love_you_gesture:
My team and I will go through them. And see what can be done.