can1357/oh-my-pi · error · ToolError
LSP action ${action} is disabled in this read-only session
Error message
LSP action ${action} is disabled in this read-only session What it means
The LSP agent tool blocks any action not in the read-only allowlist (diagnostics, definition, references, hover, symbols, status, capabilities, …) when the session is in lspReadOnly mode. This guards read-only agent sessions from mutating operations such as rename or apply of workspace edits.
Source
Thrown at packages/coding-agent/src/lsp/tool.ts:209
constructor(private readonly session: ToolSession) {
this.description = prompt.render(lspDescription);
}
static createIf(session: ToolSession): LspTool | null {
return session.enableLsp === false ? null : new LspTool(session);
}
async execute(
_toolCallId: string,
params: LspParams,
signal?: AbortSignal,
_onUpdate?: AgentToolUpdateCallback<LspToolDetails>,
_context?: AgentToolContext,
): Promise<AgentToolResult<LspToolDetails>> {
const { action, file, line, symbol, query, new_name, apply, timeout } = params;
if (this.session.lspReadOnly && !LSP_READONLY_ACTIONS.has(action)) {
throw new ToolError(`LSP action ${action} is disabled in this read-only session`);
}
const timeoutSec = clampTimeout("lsp", timeout, this.session.settings.get("tools.maxTimeout"));
const timeoutSignal = AbortSignal.timeout(timeoutSec * 1000);
const callerSignal = signal;
signal = callerSignal ? AbortSignal.any([callerSignal, timeoutSignal]) : timeoutSignal;
throwIfAborted(signal);
const config = getConfig(this.session.cwd);
// Status action doesn't need a file
if (action === "status") {
const configuredNames = Object.keys(config.servers);
const lspmuxState = await detectLspmux();
const lspmuxStatus = lspmuxState.available
? lspmuxState.running
? "lspmux: active (multiplexing enabled)"
: "lspmux: installed but server not running"
: "";View on GitHub (pinned to 9690622007)
Solutions
- Use a session with lspReadOnly disabled if mutation is intended.
- Restrict the task to read-only actions (hover, references, definition, diagnostics).
- Perform the mutation (e.g. rename) outside the restricted session via an editor or a non-read-only tool session.
- If this is your own integration, enable the write capability explicitly when constructing the LSP tool session.
Example fix
// before
await lspTool.execute({ action: "rename", file: "a.ts", line: 3, symbol: "foo", new_name: "bar" }); // read-only session
// after
const writableSession = createSession({ lspReadOnly: false });
await writableSession.lsp.execute({ action: "rename", file: "a.ts", line: 3, symbol: "foo", new_name: "bar" }); Defensive patterns
Strategy: validation
Validate before calling
const READ_ONLY = new Set(["diagnostics","definition","type_definition","implementation","references","hover","symbols","status","capabilities"]);
if (isReadOnlySession && !READ_ONLY.has(action)) throw new Error(`Action ${action} unavailable in read-only session`); Type guard
function isReadOnlyAction(action: string): boolean {
return !READ_ONLY_ACTIONS.has(action);
} Try / catch
try {
await lspTool.execute({ action: "rename", /* ... */ });
} catch (err) {
if (err instanceof ToolError && err.message.includes("read-only session")) {
// degrade: report to the agent that mutation is disabled
} else throw err;
} Prevention
- Check session capabilities before prompting an agent to mutate code.
- Keep write actions out of read-only subagent prompts.
- Encode allowed actions in your agent's system prompt/planner.
When it happens
Trigger: Calling execute() with an action like `rename`, `apply`, or another write-tier action while this.session.lspReadOnly is true — typically a subagent or session configured with read-only LSP access.
Common situations: Running an agent in restricted/read-only mode but prompting it to perform a rename; a subagent inheriting a read-only session attempting workspace edits; automation scripts using a hardened session for refactoring.
Related errors
- symbol is required for project-aware ${action}; pass symbol=
- LSP ${action} timed out after ${timeoutSec}s on ${serverName
- read-only run: '${method}' requires read_only: false
- {}: {error}
- inter-device move failed: {} to {}; unable to remove target:
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/dbdf195e6ec14b65.
Report an issue: GitHub.