# Command Timeout (/sdks/kotlin/how-to/rpc/command-timeout)



## Overview [#overview]

A **command timeout** is the deadline you attach to a single RPC call so a caller never blocks forever waiting on a handler that isn't there or isn't responding. Commands are synchronous by design — the sender is suspended until a reply arrives — so without a bound, a missing subscriber or a crashed handler turns one request into an indefinite hang that ties up a coroutine and cascades into upstream timeouts.

The timeout is set per call with `timeoutMs` on the command builder, and it's enforced by the broker itself, not by client-side polling: the broker tracks the deadline server-side and fails the request the moment it expires, regardless of what the calling coroutine is doing. When the window elapses with no response, `sendCommand` returns a response with a populated `error` instead of a clean success — your signal to retry or fall back.

**Gotchas:** a command timeout is a broker-enforced deadline, not a coroutine `withTimeout` or job cancellation, so don't assume cancelling the coroutine also stops the broker from waiting; a slow-but-alive handler and a completely absent one produce the *same* timeout error, so you can't tell them apart from the response alone; and setting `timeoutMs` too short under normal load turns transient latency into false failures.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* Kotlin SDK installed (`implementation("io.kubemq.sdk:kubemq-sdk-kotlin:1.0.1")`)

## Code [#code]

```kotlin title="CommandTimeoutExample.kt"
package io.kubemq.sdk.examples.commands

import io.kubemq.sdk.client.KubeMQClient
import io.kubemq.sdk.cq.commandMessage
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

private const val ADDRESS = "localhost:50000"
private const val CLIENT_ID = "kotlin-commands-command-timeout"
private const val CHANNEL = "kotlin-commands.command-timeout"

fun main() = runBlocking {
    val client = KubeMQClient.cq {
        address = ADDRESS
        clientId = CLIENT_ID
    }

    client.use {
        try {
            client.createCommandsChannel(CHANNEL)

            // Subscribe to handle commands (handler intentionally delays 3s)
            val subJob = launch {
                client.subscribeToCommands {
                    channel = CHANNEL
                }.collect { cmd ->
                    println("Handler received: ${String(cmd.body)}, delaying 3s...")
                    delay(3000)
                    val response = cmd.respond {
                        executed = true
                    }
                    client.sendCommandResponse(response)
                    println("Handler sent response.")
                }
            }

            delay(500)

            // Send command with short timeout (expect timeout)
            println("Sending command with 1s timeout (handler takes 3s)...")
            try {
                val resp = client.sendCommand(commandMessage {
                    channel = CHANNEL
                    body = "Slow command".toByteArray()
                    timeoutMs = 1000
                })
                if (!resp.executed && resp.error.isNotEmpty()) {
                    println("Timeout (expected): ${resp.error}")
                    println("  executed=${resp.executed}")
                } else {
                    println("Unexpected success: executed=${resp.executed}")
                }
            } catch (e: Exception) {
                println("Exception (timeout): ${e.message}")
            }

            // Wait for handler to finish before sending second command
            delay(4000)

            // Send command with sufficient timeout (expect success)
            println("\nSending command with 5s timeout (handler takes 3s)...")
            try {
                val resp = client.sendCommand(commandMessage {
                    channel = CHANNEL
                    body = "Normal command".toByteArray()
                    timeoutMs = 5000
                })
                println("Success: executed=${resp.executed}")
            } catch (e: Exception) {
                println("Error: ${e.message}")
            }

            subJob.cancel()
        } finally {
            try { client.deleteCommandsChannel(CHANNEL) } catch (_: Exception) {}
        }
        println("Done.")
    }
}
```

## How It Works [#how-it-works]

* The handler intentionally delays 3 seconds to simulate slow processing.
* A 1-second timeout expires before the handler responds, resulting in a timeout error.
* A 5-second timeout allows the handler enough time to respond successfully.
* Set `timeoutMs` based on expected processing time plus network latency.

## Related [#related]

* [Send Command](/sdks/kotlin/tutorials/command-send)
* [Handle Command](/sdks/kotlin/how-to/rpc/command-handle)
* [RPC Reference](/sdks/kotlin/reference/rpc)
