TL;DR — En Claude Code sobre VS Code dejó de funcionar el scroll con la rueda del ratón. La causa no fue un bug espontáneo: fue un parche que yo mismo había aplicado días antes para otro problema (códigos de mouse que se tecleaban solos y quemaban tokens). Resulta que el scroll y esa fuga de tokens son el mismo interruptor:
CLAUDE_CODE_DISABLE_MOUSE. Al matar la fuga, maté el scroll. La solución fina fue cambiar aCLAUDE_CODE_DISABLE_MOUSE_CLICKS=1, que conserva el scroll y quita el tracking de movimiento (que era lo que fugaba). Aquí está la historia completa, reproducible.
1. El síntoma
Estaba trabajando en un plan largo dentro de Claude Code (el TUI corriendo en la terminal integrada de VS Code) y, de la noche a la mañana, ya no podía scrollear hacia arriba con la rueda del ratón para releer el plan. La rueda simplemente no movía el contenido.
En la barra inferior de la terminal aparecía este aviso, que resultó ser la pista clave:
Scroll wheel is sending arrow keys · use PgUp/PgDn to scroll
Traducido: “la rueda del ratón está enviando teclas de flecha; usa RePág/AvPág para desplazarte”. Es decir, la rueda ya no scrolleaba: se estaba convirtiendo en pulsaciones de flecha ↑↓.
El contexto que despistaba
Lo que hacía difícil el diagnóstico es que venía de otro problema previo. Días antes había pasado esto:
- Al mover el ratón sobre la terminal, aparecían tecleados solos, en ráfaga, caracteres raros como
^[[<35;117;62M. - Si esa ráfaga caía dentro del prompt de Claude Code, se enviaba al modelo como si yo hubiera escrito algo → respuestas basura y consumo de tokens sin que yo tocara el teclado.
- Para “matar” eso, apliqué un parche. Y justo después de ese parche fue cuando el scroll dejó de funcionar.
La sensación era “arreglé una cosa y se rompió otra sin relación”. Spoiler: sí estaban relacionadas. Eran, literalmente, la misma perilla.
2. Entendiendo los códigos raros (^[[<35;...M)
Antes de la solución, vale la pena entender qué eran esos caracteres, porque explican todo.
^[[<35;117;62M
^[es la tecla ESC (byte0x1B).- El patrón completo es:
ESC [ < Cb ; Ccol ; Cfila M. - Eso es un evento de mouse de xterm en modo SGR (1006).
Cb = 35significa “ratón moviéndose sin botón presionado”. Los valores0/2serían clics de botón, y64/65serían la rueda arriba/abajo.
O sea: la terminal estaba en un modo donde reporta cada movimiento del ratón como una secuencia de escape. Normalmente una aplicación TUI (como Claude Code) activa ese modo mientras corre y lo apaga al salir. El problema aparece cuando la app no lo apaga — típicamente porque la sesión se cerró a la fuerza:
- se cerró la ventana de la terminal con Claude vivo,
- un
taskkill /Fsobre el proceso, - la terminal se congeló o crasheó.
Con el modo quedándose prendido, cualquier shell en esa consola sigue emitiendo esos eventos de movimiento como si fueran tecleados. De ahí la fuga.
Regex para detectar el síntoma (útil si quieres automatizar la detección):
\x1b\[<\d+;\d+;\d+[Mm]
3. El parche original (que causó el efecto colateral)
Para eliminar la fuga, lo que había hecho fue desactivar por completo la captura de mouse del TUI de Claude Code con una variable de entorno de usuario persistente:
[Environment]::SetEnvironmentVariable("CLAUDE_CODE_DISABLE_MOUSE","1","User")
Y como respaldo, en el perfil de PowerShell (C:\Users\rgarc\Documents\WindowsPowerShell\profile.ps1) había:
$env:CLAUDE_CODE_DISABLE_MOUSE = "1"
Más una función de rescate para “destorar” una terminal ya afectada:
function reset-term {
$e = [char]27
# Apaga mouse tracking (1000/1002/1003), SGR-mouse (1006), urxvt (1015),
# focus-reporting (1004), bracketed-paste (2004) y muestra el cursor (25h).
Write-Host -NoNewline "$e[?1000l$e[?1002l$e[?1003l$e[?1006l$e[?1015l$e[?1004l$e[?2004l$e[?25h"
Write-Host "reset-term: reporte de mouse desactivado."
}
Set-Alias reset-mouse reset-term
Funcionó para la fuga. Pero CLAUDE_CODE_DISABLE_MOUSE=1 no apaga solo el tracking de movimiento: apaga toda la captura de mouse del TUI, incluida la rueda de scroll.
4. La causa raíz: el scroll y la fuga son el mismo interruptor
Aquí está la lección central de todo esto:
- Con
CLAUDE_CODE_DISABLE_MOUSE=1, Claude Code no captura la rueda. - Como Claude Code corre en la “pantalla alterna” (alternate screen), VS Code no tiene un buffer que scrollear ahí, así que traduce la rueda a teclas de flecha ↑↓.
- Por eso el aviso: “Scroll wheel is sending arrow keys”.
Los dos problemas tienen arreglos opuestos, y eso es lo que confunde:
| Problema | Dónde ocurre | Causa | Arreglo |
|---|---|---|---|
Se tecleaban ^[[<35;...M solos |
En el shell, tras cerrar Claude a la fuerza | Mouse tracking quedó ENCENDIDO | Apagar el mouse (reset-term) |
| No hace scroll la rueda | Dentro del TUI de Claude | Mouse tracking está APAGADO (la env var) | Encender el mouse |
El parche resolvió el primero de forma permanente… y con ello activó el segundo. Misma perilla, efectos opuestos.
5. La solución: conservar el scroll, matar solo el movimiento
La clave estaba en no usar el interruptor “todo o nada”. Claude Code tiene una variable más granular:
# Conserva el scroll, quita clics y tracking de movimiento (lo que fugaba):
[Environment]::SetEnvironmentVariable("CLAUDE_CODE_DISABLE_MOUSE_CLICKS","1","User")
# Borrar la variable vieja "todo o nada":
[Environment]::SetEnvironmentVariable("CLAUDE_CODE_DISABLE_MOUSE",$null,"User")
¿Por qué esto es el mejor de ambos mundos?
- La rueda (eventos
Cb=64/65) vuelve a scrollear dentro de Claude. ✅ - Se quita el tracking de movimiento (
Cb=35), que era exactamente lo que generaba la fuga^[[<35;...Mal mover el ratón. ✅ - Los clics se desactivan, pero para un flujo de trabajo de teclado eso no estorba.
Cambios exactos aplicados
- Registro de usuario (persistente): borrada
CLAUDE_CODE_DISABLE_MOUSE, creadaCLAUDE_CODE_DISABLE_MOUSE_CLICKS=1. Verificado. profile.ps1actualizado para limpiar el var viejo si se hereda y poner el nuevo:Remove-Item Env:\CLAUDE_CODE_DISABLE_MOUSE -ErrorAction SilentlyContinue # limpia el de sesión heredado $env:CLAUDE_CODE_DISABLE_MOUSE_CLICKS = "1" # respaldoreset-termse conserva como red de seguridad para cierres a la fuerza.- (Actualización 2026-07-12) La variable quedó además en el bloque
envde~/.claude/settings.json— ver la sección siguiente; esa es ahora la vía principal y el registro/perfil quedan de respaldo.
5.1. La vía canónica: hacerlo global en settings.json
Al validar el cambio descubrí que el registro + profile.ps1 tienen huecos: el perfil solo aplica a terminales PowerShell (no cmd ni Git Bash), y VS Code cachea el entorno al arrancar. Claude Code tiene su propio mecanismo global que evita todo eso: un bloque env en C:\Users\rgarc\.claude\settings.json:
{
"env": {
"CLAUDE_CODE_DISABLE_MOUSE_CLICKS": "1"
}
}
Con esto, toda sesión de claude arranca con la variable puesta, sin importar desde qué shell la lances ni qué entorno haya cacheado VS Code. Es la forma recomendada; las otras dos capas no estorban y quedan como respaldo.
6. Cómo activarlo (detalle importante)
Las variables de entorno se leen al arrancar el proceso. La sesión de Claude que ya estaba abierta seguía con el valor viejo, así que el cambio no aplica en caliente. Para activarlo:
- Salir limpio de Claude:
Ctrl+D(oCtrl+Cdos veces). Nunca cerrar la ventana con la sesión viva. - Cerrar VS Code por completo y reabrirlo (VS Code cachea el entorno al arrancar; reabrir refresca el registro y elimina rastros del var viejo).
- Abrir una terminal nueva y correr
claude. La rueda ya scrollea.
Alternativa sin cerrar VS Code entero: abrir una terminal nueva y correr
claude. Elprofile.ps1corregido limpia el var viejo heredado y pone el nuevo. Cerrar/reabrir VS Code es solo la vía a prueba de balas.
La trampa al validar: ventana nueva ≠ proceso nuevo
Aquí caí al intentar comprobar el fix: abrí una ventana nueva de VS Code esperando ver el cambio… y nada. La razón: en Windows, “File → New Window” no crea un proceso nuevo — la ventana se cuelga de la primera instancia de VS Code que abriste, y hereda el entorno que esa instancia cacheó al arrancar. Si tenías ventanas abiertas desde antes del cambio, la “nueva” nace con el entorno viejo. Para que una ventana de VS Code vea variables de entorno frescas hay que cerrar todas las ventanas primero (matar el proceso completo).
Con el bloque env de settings.json (sección 5.1) esta trampa deja de importar: Claude Code se pone la variable a sí mismo al arrancar, herede lo que herede de VS Code. Basta salir de la sesión vieja (Ctrl+D) y volver a correr claude en la misma terminal.
Cómo saber que funcionó
La prueba es dentro del TUI, no en el shell: la rueda del ratón scrollea el contenido y ya no aparece el aviso “Scroll wheel is sending arrow keys” en la barra de la terminal. Que “no pase nada raro” al mover el ratón también es éxito: la fuga ^[[<35;...M solo se manifestaba tras cerrar una sesión a la fuerza.
Mientras tanto, dentro de una sesión que arrancó con el valor viejo, para leer hacia arriba se usa PgUp / PgDn (o Shift+PgUp).
7. Referencia rápida de variables
| Variable | Efecto | ¿Rueda scrollea? | ¿Frena la fuga por movimiento? |
|---|---|---|---|
CLAUDE_CODE_DISABLE_MOUSE=1 |
Apaga todo el mouse del TUI | ❌ No | ✅ Sí (pero rompe scroll) |
CLAUDE_CODE_DISABLE_MOUSE_CLICKS=1 |
Quita clics + movimiento, mantiene scroll | ✅ Sí | ✅ Sí ← el que usamos |
CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1 |
Fuerza modo clásico (sin pantalla alterna) | (depende) | — |
Si tu versión de Claude Code es vieja y no reconoce alguna variable, no hace daño: simplemente se ignora. (Probado en Claude Code 2.1.178, Windows 11 Pro, Windows PowerShell 5.)
8. Rescate manual (si una terminal queda “atorada”)
Si algún día cierras Claude a la fuerza y una terminal empieza a teclear códigos raros al mover el ratón, en el prompt de PowerShell (no dentro del TUI):
reset-term
# equivalente manual:
$e=[char]27; Write-Host -NoNewline "$e[?1000l$e[?1002l$e[?1003l$e[?1006l$e[?1015l$e[?1004l$e[?2004l$e[?25h"
Lo más confiable en Windows sigue siendo cerrar esa pestaña y abrir una nueva.
9. Lecciones (lo que me llevo)
- Un “arreglo” puede ser la causa del siguiente bug. Cuando algo se rompe “de la nada”, revisa primero lo último que tocaste, aunque parezca no relacionado.
- Cuidado con los interruptores “todo o nada”.
DISABLE_MOUSEapagaba movimiento, clics y scroll de un golpe. Casi siempre existe una perilla más fina (DISABLE_MOUSE_CLICKS). - Los mensajes de la propia herramienta valen oro. El aviso “Scroll wheel is sending arrow keys” prácticamente resolvió el caso.
- Higiene de cierre importa. Salir de Claude con
Ctrl+D/Ctrl+C(nunca cerrar la ventana en caliente) evita que el modo de mouse quede prendido y cause la fuga. - Documenta el porqué, no solo el qué. Guardé el diagnóstico para no volver a caer en la misma confusión — de ahí salió este post.
Entorno probado: Windows 11 Pro (10.0.26200) · Windows PowerShell 5 · VS Code (terminal integrada) · Claude Code 2.1.178 (Opus 4.8).