Skip to content

Unrecognized variable in an input emits an unbalanced ], producing invalid project.json #370

Description

@juanmajavaloyes

Repro

Same shape as the repro in #302:

tmp=$(mktemp -d /tmp/goboscript-name-json.XXXXXX)
printf 'costumes "blank.svg";\n' > "$tmp/stage.gs"
printf 'costumes "blank.svg";\nonflag { say fantasma; }\n' > "$tmp/main.gs"
touch "$tmp/blank.svg"
cargo run --quiet -- build "$tmp"
python3 -c "import json,zipfile,sys; json.loads(zipfile.ZipFile(sys.argv[1]).read('project.json'))" \
  "$tmp/$(basename "$tmp").sb3"

fantasma is never declared.

Actual

The compiler reports unrecognized variable for fantasma and still writes an .sb3. The generated project.json is invalid JSON:

"inputs":{"MESSAGE":[10, ""]]}}}
                           ^ unexpected extra ]

json.loads (and JSON.parse) fail at that position, so the resulting project cannot be opened by Scratch or TurboWarp.

Where it comes from

In src/codegen/input.rs, name_input writes the opening bracket only when the name resolves:

  • Some(QualifiedName::Var(..)) → writes [3,[12,name,name],
  • Some(QualifiedName::List(..)) → writes [3,[13,name,name],
  • None{}, writes nothing

and then all three arms fall through to shadow_input, which unconditionally closes with [10, ""]]. On the None arm that trailing ] has no matching [, which is exactly the malformed output above.

Expected

The same conclusion as #302: once an expression diagnostic is emitted, either no artifact should be written, or the output should stay valid JSON (for instance emitting [1,[10,""]] for the unresolved input).

Notes

  • Same malformed signature as Build emits invalid project.json after expression diagnostics #302, but a different code path: that one went through value_input (the NaN / Infinity arms), this one goes through name_input.
  • Observed at 0a592e276f4aad8f7acefb1c36d8a383dec43661 (2026-06-19, i.e. after Build emits invalid project.json after expression diagnostics #302 was closed), building the crate with wasm-pack --target web and calling build() from JavaScript rather than through the CLI — that is where the exact bytes above come from. The name_input / shadow_input code path is still present on main today, but I have not rebuilt main to confirm it at runtime, so it is possible a later change already covers it.
  • Practical impact for a downstream consumer: because an artifact is produced, a naive integration hands the .sb3 straight to a player and gets a JSON SyntaxError at load time instead of a compiler error, which is very confusing to end users. We now validate the zip's project.json before running it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions