What happened?
When lsp_diagnostics is called for a file handled by an LSP server that supports the 3.17 pull model (e.g., tsgo, the TypeScript-Go LSP), AFT sends a textDocument/diagnostic request with "identifier": null and "previousResultId": null in the params body.
Strict LSP servers reject this with InvalidParams (-32602) because JSON Schema defines those fields as string? (string-or-absent), not string-or-null. This triggers AFT's recoverable_pull_rejection path (status: pull_rejected_push_fallback), which then waits for push diagnostics that never arrive from pull-only servers. The result is empty diagnostics with complete: false and the server stuck in pending_servers with no type errors surface despite clear violations in the file.
Root cause: The upstream lsp-types crate (v0.97) is missing #[serde(skip_serializing_if = "Option::is_none")] on the identifier and previous_result_id fields of DocumentDiagnosticParams (and the identifier field of WorkspaceDiagnosticParams). Rust Option::None gets serialized as JSON null instead of the field being omitted entirely.
Impact: Affects any LSP server that strictly validates against the LSP spec schema. Currently reproducible with tsgo (TypeScript-Go), and potentially other strict LSP 3.17 servers.
Reproduction Steps:
- Configure tsgo as an LSP server in aft.jsonc:
"servers": {
"tsgo": {
"extensions": [".ts", ".tsx", ".js"],
"binary": "tsgo",
"args": ["--lsp", "--stdio"]
}
}
- Create a .ts file with a type error: const x: number = "hello";
- Run lsp_diagnostics on the file with wait_ms > 0
- Observe status: "pull_rejected_push_fallback", complete: false, diagnostics: []
Diagnostics
Plugin version
No response
AFT binary version
No response
Platform
No response
Log output (optional)
What happened?
When lsp_diagnostics is called for a file handled by an LSP server that supports the 3.17 pull model (e.g., tsgo, the TypeScript-Go LSP), AFT sends a textDocument/diagnostic request with "identifier": null and "previousResultId": null in the params body.
Strict LSP servers reject this with InvalidParams (-32602) because JSON Schema defines those fields as string? (string-or-absent), not string-or-null. This triggers AFT's recoverable_pull_rejection path (status: pull_rejected_push_fallback), which then waits for push diagnostics that never arrive from pull-only servers. The result is empty diagnostics with complete: false and the server stuck in pending_servers with no type errors surface despite clear violations in the file.
Root cause: The upstream lsp-types crate (v0.97) is missing #[serde(skip_serializing_if = "Option::is_none")] on the identifier and previous_result_id fields of DocumentDiagnosticParams (and the identifier field of WorkspaceDiagnosticParams). Rust Option::None gets serialized as JSON null instead of the field being omitted entirely.
Impact: Affects any LSP server that strictly validates against the LSP spec schema. Currently reproducible with tsgo (TypeScript-Go), and potentially other strict LSP 3.17 servers.
Reproduction Steps:
Diagnostics
Plugin version
No response
AFT binary version
No response
Platform
No response
Log output (optional)