tool4d 21R3 crashes (0xC0000005) servicing workspace/diagnostic when the workspace is not a formal 4D project
Summary
With 4D-Analyzer.diagnostics.scope set to Workspace (the default), the tool4d language server crashes with an access violation (0xC0000005, Windows exit code 3221225477) immediately after receiving a workspace/diagnostic request, when the opened folder is a plain folder that contains loose .4dm file(s) but is not a structured 4D project (no Project/, no .4DProject).
Single-file diagnostics (textDocument/diagnostic) work fine; only the workspace-wide scan crashes. The result is a language server that dies on startup and cannot recover ("Client is not running", Cannot call write after a stream was destroyed).
This is a regression: tool4d 21 (build 100422) does not crash under the same conditions; 21R3 (build 100164) does.
Environment
|
|
| Extension |
4D-Analyzer 0.2.9 |
| tool4d |
21R3 build 100164 (serverInfo version 2130) |
| Known-good tool4d |
21 build 100422 (no crash) |
| VS Code |
1.127.0 |
| OS |
Windows 11 Home 10.0.26200 (x64) |
| Setting |
4D-Analyzer.diagnostics.scope = Workspace (default) |
Steps to reproduce
- Open a folder that is not a 4D project (e.g. any folder containing one loose
.4dm file among unrelated files — in my case a Go project).
- Ensure
4D-Analyzer.diagnostics.scope = Workspace (default) and tool4d version resolves to 21R3.
- Open the
.4dm file so the extension activates.
Actual result
The server crashes. From the verbose LSP trace:
Sending request 'initialize - (0)' -> OK (31ms)
Sending notification 'textDocument/didOpen'
Sending request 'textDocument/diagnostic (1)' -> OK (28ms), no result
Sending request 'workspace/diagnostic (2)'
[Error] Client 4D-LSP: connection to server is erroring. read ECONNRESET
Language server exited with exit code 3221225477 (0xC0000005 access violation)
Expected result
workspace/diagnostic over a non-4D-project folder should return diagnostics (or an empty result), not crash the server.
Minimal reproduction (isolated from VS Code)
The crash is reproducible by driving tool4d directly over the LSP socket. The critical detail is that the client must answer the server's workspace/configuration callback — that is what makes tool4d actually walk the workspace tree, and the walk is what faults. A client that ignores the callback returns too fast and does not crash.
Node harness (node repro.js <path-to-tool4d.exe>):
const net = require('net');
const { spawn } = require('child_process');
const rootUri = "file:///c%3A/path/to/non-4d-project-folder";
const docUri = rootUri + "/some.4dm";
const cfg = { trace:{server:"off"}, server:{path:"",tool4d:{enable:true,version:"latest",channel:"stable",location:""}},
diagnostics:{enable:true, scope:"Workspace"}, dependencies:{debug:false} };
function frame(o){const s=JSON.stringify(o);return `Content-Length: ${Buffer.byteLength(s)}\r\n\r\n${s}`;}
net.createServer(sock=>{
let buf=Buffer.alloc(0);
const send=o=>sock.write(frame(o));
sock.on('data',d=>{ buf=Buffer.concat([buf,d]);
while(true){ const h=buf.indexOf("\r\n\r\n"); if(h<0)break;
const m=buf.slice(0,h).toString().match(/Content-Length:\s*(\d+)/i); if(!m)break;
const len=+m[1],s=h+4; if(buf.length<s+len)break;
const msg=JSON.parse(buf.slice(s,s+len)); buf=buf.slice(s+len);
if(msg.method==="workspace/configuration") send({jsonrpc:"2.0",id:msg.id,result:(msg.params.items||[{}]).map(()=>cfg)});
else if(msg.method && msg.id!=null) send({jsonrpc:"2.0",id:msg.id,result:null});
}});
send({jsonrpc:"2.0",id:0,method:"initialize",params:{processId:process.pid,rootUri,
capabilities:{textDocument:{diagnostic:{}},workspace:{configuration:true,workspaceFolders:true}},
initializationOptions:cfg, workspaceFolders:[{uri:rootUri,name:"repro"}]}});
setTimeout(()=>{ send({jsonrpc:"2.0",method:"initialized",params:{}});
send({jsonrpc:"2.0",method:"textDocument/didOpen",params:{textDocument:{uri:docUri,languageId:"4d",version:1,text:"//x\n"}}}); },300);
setTimeout(()=>{ send({jsonrpc:"2.0",id:2,method:"workspace/diagnostic",params:{previousResultIds:[],partialResultToken:"t"}}); },1200);
}).listen(0,'127.0.0.1',function(){
const child=spawn(process.argv[2],["--lsp="+this.address().port]);
child.on('exit',c=>console.log("tool4d exit",c,"0x"+(c>>>0).toString(16)));
});
Observed: with tool4d 21R3 → tool4d exit 3221225477 0xc0000005. With tool4d 21 → no crash.
Workaround
Set 4D-Analyzer.diagnostics.scope to Document. This suppresses the workspace/diagnostic request; single-file diagnostics and all other language features continue to work, and the server no longer crashes.
tool4d 21R3 crashes (0xC0000005) servicing
workspace/diagnosticwhen the workspace is not a formal 4D projectSummary
With
4D-Analyzer.diagnostics.scopeset toWorkspace(the default), the tool4d language server crashes with an access violation (0xC0000005, Windows exit code3221225477) immediately after receiving aworkspace/diagnosticrequest, when the opened folder is a plain folder that contains loose.4dmfile(s) but is not a structured 4D project (noProject/, no.4DProject).Single-file diagnostics (
textDocument/diagnostic) work fine; only the workspace-wide scan crashes. The result is a language server that dies on startup and cannot recover ("Client is not running",Cannot call write after a stream was destroyed).This is a regression: tool4d 21 (build 100422) does not crash under the same conditions; 21R3 (build 100164) does.
Environment
2130)4D-Analyzer.diagnostics.scope=Workspace(default)Steps to reproduce
.4dmfile among unrelated files — in my case a Go project).4D-Analyzer.diagnostics.scope=Workspace(default) and tool4d version resolves to 21R3..4dmfile so the extension activates.Actual result
The server crashes. From the verbose LSP trace:
Expected result
workspace/diagnosticover a non-4D-project folder should return diagnostics (or an empty result), not crash the server.Minimal reproduction (isolated from VS Code)
The crash is reproducible by driving tool4d directly over the LSP socket. The critical detail is that the client must answer the server's
workspace/configurationcallback — that is what makes tool4d actually walk the workspace tree, and the walk is what faults. A client that ignores the callback returns too fast and does not crash.Node harness (
node repro.js <path-to-tool4d.exe>):Observed: with tool4d 21R3 →
tool4d exit 3221225477 0xc0000005. With tool4d 21 → no crash.Workaround
Set
4D-Analyzer.diagnostics.scopetoDocument. This suppresses theworkspace/diagnosticrequest; single-file diagnostics and all other language features continue to work, and the server no longer crashes.