AI refac

بِسْمِ ٱللَّهِ ٱلرَّحْمَـٰنِ ٱلرَّحِيمِ

Cost and Troubleshooting

Why a Kotlin change is slow, what to do when a single change is refused, and how to fix the server, memory, timeout, and Gradle problems.

This page will inshallah help when a Kotlin command is refused, slow, or stuck.

A single change is refused

A single Kotlin move or rename is an error before anything starts, dry runs included. A folder, several files, and a batch are not refused. The message writes the batch command out for your request and carries a long explanation, the same text as refac guide kotlin. Nothing is changed. The options, best first:

  1. Collect the changes and make them in one call. Moves repeat the flags or take a list from a file. Renames take a list, one language per list.

    refac move --batch moves.json
    echo '[{"file": "a.kt", "symbol": "old", "new_name": "new"}]' | refac rename --batch -

    A folder is one change that already moves all its files in one start. Do the renames first, because they do not change paths, and then one move call. That is two starts instead of one per change.

  2. One change is all there is. Run the same command again with --allow-single. It pays the start once. A dry run is no shortcut: it makes the real change on a copy of the project to learn the plan, pays the start, and the real run pays it again.

  3. Several separate commands cannot be avoided, for example in a script or a tool loop. Set REFAC_KOTLIN_GRADLE_IDLE_SECS=120 (1 to 3600) in the environment. The Gradle daemon of the project then stays up that many seconds after the command, the next command skips the cold import, and 24 seconds become about 15. The price is the daemon's memory, several hundred MB, for those seconds after every command. It is off by default, and the daemon stops 10 seconds after the import.

  4. You do not want the refusal at all. Set REFAC_KOTLIN_BATCH_ONLY=0. This is a person's choice and not an agent's, because the refusal is what keeps a series of single changes from costing minutes. The variable is 1 (also when empty or unset) or 0, and anything else is an error. A single change that gets through ends with a note that gives the seconds it took and the batch form.

  5. Where the index lives. REFAC_KOTLIN_CACHE=<directory> moves it and REFAC_KOTLIN_CACHE=off keeps nothing, which makes every start about 20 seconds slower. It is deleted when it passes 2 GB. A slow machine can raise REFAC_KOTLIN_TIMEOUT_SECS (default 600).

What does not help: a build cache or task runner such as Gradle's build cache, Moon, or Nx. They replay work whose inputs did not change, and here every command changes the inputs. The cost is the server start and not a build. A server that stays resident between commands would remove the start, but it would hold 1.3 to 1.8 GB per project all day, and refac does not offer one.

If you are an agent: do not call Kotlin changes one by one. Collect everything first and make one call. If you only found out that there are more changes after the first one, finish the series in one call and say so. Do not turn the refusal off to keep going.

Why it is slow

Moving a Kotlin file and fixing every import, or renaming a symbol in every Kotlin and Java file that uses it, needs a compiler front end that understands the Gradle build, Java interop, and Android. The only server that does is the JetBrains Kotlin language server. refac starts it for the command and stops it when the command ends, so a single change pays the whole start. Measured on a small project:

StepTime
JVM and initializeabout 4 s
Gradle import of the project3 s with a running Gradle daemon, 11 s with a cold one
Indexing the JDK and the Kotlin librariesabout 19 s once. refac keeps the index in ~/.cache/refac, so later calls skip most of it
Waiting until the server says it is readyabout 6 s
The change itself2 to 4 s

The first call on a machine takes about 44 seconds, the calls after it about 24, and about 15 when the Gradle daemon of the call before is still up. The server and the Gradle daemon hold 1.3 to 1.8 GB while the command runs, and a big project holds more.

As an estimate and not a measurement, twenty single changes as twenty commands take about 8 minutes (20 times 24 s). The same twenty in one call take about a minute and a half, because the start is paid once and each change then costs about 3 seconds.

The server is missing or broken

refac doctor kotlin

It looks for the server where a command would, starts it, waits for the handshake, stops it, and prints the numbered steps to fix what is wrong. It also checks that a JDK 17 or newer is on the PATH. Android projects need an Android SDK as well (ANDROID_HOME or local.properties).

REFAC_KOTLIN_SERVER beats every other place. If it is set to a wrong folder, refac reports it as wrong and does not look elsewhere. The folder must hold bin/intellij-server and a build.txt that starts with ILS-.

The verified server is an early-access build from JetBrains. It asks for no license agreement, but its bundled key has an end date. When a start fails with a license message, install a newer build. refac doctor kotlin prints the steps, and the next start should inshallah work.

The Gradle import fails

A broken Gradle build ends the wait early, and refac prints the tail of the import log. For more of it, run the command with RUST_LOG=debug, which prints the Gradle import output. For a Multiplatform build whose source sets do not follow the names commonMain, commonTest, or <target>Main, the import prints Failed to find 'target' in Kotlin extension, and refac stops with an error that says so.

The server died

When the language server dies, for example from an out-of-memory kill or a JVM crash, the error names how the process ended and shows the last lines it wrote to stderr. Signal 9 is usually the Linux out-of-memory killer. The server needs 1.3 to 1.8 GB and the project's Gradle daemon comes on top, so close what you can or try again on a machine with more memory.

It does not answer

A server that does not answer is an error after REFAC_LSP_TIMEOUT_SECS (default 300), and the Kotlin Gradle import after REFAC_KOTLIN_TIMEOUT_SECS (default 600). It is never a hang. REFAC_LSP_TRACE=1 prints every message exchanged with the server.

The index cache

The Kotlin server's index (110 to 160 MB) is the only thing refac keeps on your machine. It lives in ~/.cache/refac, in a folder per server build, and is deleted when it passes 2 GB. If a start misbehaves, delete it and try again:

rm -rf ~/.cache/refac/kotlin-*

A copy that cannot be read is an error that says so.

On this page