secondlife / secondlife/viewer

object.content.save returns `-32603 Internal error: stoi` instead of diagnostics when LSL fails to compile

Open
#6,259 0 comments 0 reactions 1 assignee View on GitHub

@marchcat is already working on this.

Since Sep 8, 2026.

  • #6284 by @marchcat — merged
bug
Dominant language
C++
Stars
299
Forks
146
Avg merge
1d 9h
Merged PRs (30d)
88

Description

Environment

Second Life Release 26.4.0.33661928315, Linux x86_64

Description

Summary

Saving syntactically invalid LSL through the script-editor JSON-RPC server
returns a JSON-RPC internal error rather than the documented
{compiled: false, diagnostics: [...]} response. The client is told the request
failed, but not that the script failed to compile, and never receives the
error location or message.

Valid source saves correctly, so the save path itself works. Only the
compile-error path fails.

Environment

  • Viewer: Second Life Release 26.4.0.33661928315, Linux x86_64
  • Script editor server started via Tools > Script Editor Server (port 9020)
  • Client: a JSON-RPC client of our own, which completes session.handshake
    (challenge echoed) and receives session.ok normally
Reproduction steps

Steps to Reproduce

  1. Rez a prim you own on a parcel where scripts are enabled.
  2. object.request on it (publishes; returns success: true).
  3. object.item.create with {prim_id, name, type: "script", vm: "mono"} — returns the new item with its item_id.
  4. object.content.save with {prim_id, item_id, content} where content is
    syntactically invalid LSL.

Observed

{"jsonrpc": "2.0", "id": 106,
 "error": {"code": -32603, "message": "Internal error: stoi"}}

Expected

Per the protocol documentation and the unified_diagnostics feature advertised
in the handshake:

{"compiled": false,
 "diagnostics": [{"row": 5, "column": 21, "level": "error", "message": "..."}]}

Reproduces across all five error classes we tried

Each of these, saved alone, produces the identical stoi error:

// 1. missing semicolon
default { state_entry() { llSay(0, "x") } }

// 2. undeclared variable
default { state_entry() { integer n = zzz; } }

// 3. unknown function
default { state_entry() { llNotARealFunction(); } }

// 4. unclosed brace
default { state_entry() { llSay(0, "x");

// 5. two errors at once
default
{
    state_entry()
    {
        integer n = notAThing + 1;
        llSay(0, "no semicolon after this")
    }
}

Positive control

The same call with valid source succeeds every time:

{"compiled": true, "success": true,
 "item_id": "...", "prim_id": "..."}

and the script's state_entry output appears in local chat, confirming the new
source really did compile and run. So the failure is specific to the
compile-error path, not to the save request, the params, or the connection.

Variables ruled out

  • Parcel: reproduces on a scripts-disabled parcel and a scripts-enabled one.
  • Locale: our first runs logged Failed to set locale en_US.utf8 26,600
    times (an FHS container with only C/POSIX available). Re-running with
    LANG=C produced zero locale warnings and the identical stoi error, so
    locale is not involved.
  • Upload path: the viewer log shows the invalid source uploading normally
    (Uploading: Type: lsltext, UUID: ...) before the error is returned, so the
    asset reaches the simulator and the failure is on the way back.

Possibly relevant

indra/newview/llscripteditorws.cpp parses the compile error location with:

static const boost::regex LSL_LOCATION_PATTERN(R"(\((\d+), (\d+)\) : ([^:]+) : (.+))");
...
diagnostic["row"]    = std::stoi(match[1].str()) + 1;
diagnostic["column"] = std::stoi(match[2].str()) + 1;

Those stoi calls in saveScript appear to have been added by ece8b732
("Extract the correct compile error location from message.", 2026-08-19);
before that commit this path emitted row = 0, column = 0 and called stoi at
all.

We have not confirmed which line throws. The \d+ capture groups suggest a
successful regex_match should make stoi safe, so the interesting question is
probably which compiler error format fails to match LSL_LOCATION_PATTERN — the
five samples above should make that identifiable. We are reporting the
reproduction rather than a diagnosis.

Impact

This is the compile-feedback path for any external editor using this protocol,
including the official VS Code extension. An editor cannot show compile errors,
and cannot distinguish "your script has a syntax error" from "the connection
broke" — both arrive as the same internal error.

Related

Possibly the same area as #6189 (two divergent compile-feedback paths,
sendCompileResults vs handleObjectContentSave) and #6180
(object.content.save behaviour when running is omitted).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.