> ## Documentation Index
> Fetch the complete documentation index at: https://taskdaemon.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Handler Overview

> Write task handlers in any programming language

# Handler Overview

TaskDaemon handlers are containerized programs that process tasks. They communicate via a simple JSON protocol over stdin/stdout, making it possible to write handlers in **any programming language**.

## How Handlers Work

```
TaskDaemon                    Handler Container
    │                              │
    │──── JSON task ──────────────▶│ (stdin)
    │                              │
    │                         Process task
    │                              │
    │◀─── JSON result ────────────│ (stdout)
    │                              │
```

1. TaskDaemon sends a JSON task to the handler's stdin
2. Handler processes the task
3. Handler writes a JSON result to stdout
4. TaskDaemon captures the result

## Protocol

### Input (stdin)

```json theme={null}
{
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "task_type": "process",
  "task_data": {"key": "value"},
  "attempt": 1
}
```

### Output (stdout)

**Success:**

```json theme={null}
{"status": "success", "result": {"processed": true}}
```

**Error (retryable):**

```json theme={null}
{"status": "error", "error": "Connection timeout", "retryable": true}
```

**Error (permanent):**

```json theme={null}
{"status": "error", "error": "Invalid input", "retryable": false}
```

## Official SDKs

We provide SDKs that handle the protocol for you:

<CardGroup cols={2}>
  <Card title="Python" icon="python" href="/handlers/python">
    `pip install taskdaemon`
  </Card>

  <Card title="Node.js" icon="node-js" href="/handlers/nodejs">
    `npm install @taskdaemon/handler`
  </Card>

  <Card title="Go" icon="golang" href="/handlers/go">
    `go get github.com/taskdaemon/handler-go`
  </Card>

  <Card title="Rust" icon="rust" href="/handlers/rust">
    `cargo add taskdaemon-handler`
  </Card>

  <Card title="C++" icon="c" href="/handlers/cpp">
    Header-only library
  </Card>

  <Card title="Java" icon="java" href="/handlers/java">
    Maven/Gradle package
  </Card>

  <Card title="C#" icon="microsoft" href="/handlers/csharp">
    NuGet package
  </Card>

  <Card title="Raw Protocol" icon="code" href="/handlers/raw-protocol">
    Any language
  </Card>
</CardGroup>

## Handler Requirements

<Steps>
  <Step title="Read JSON from stdin">
    Handler must continuously read line-delimited JSON from stdin
  </Step>

  <Step title="Process the task">
    Parse the task data and perform the work
  </Step>

  <Step title="Write JSON to stdout">
    Output a single JSON line with status and result
  </Step>

  <Step title="Flush output">
    Ensure stdout is flushed after each response
  </Step>
</Steps>

<Info>
  **Print statements are safe.** TaskDaemon automatically skips non-JSON lines in handler output, so `print()` statements for debugging won't break the protocol. However, for production use, [logging to stderr](#logging-to-stderr) is recommended.
</Info>

## Logging to stderr

For production handlers, log to stderr instead of stdout to keep logs separate from the JSON protocol:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import sys
    print("Debug info", file=sys.stderr)

    # Or use logging
    import logging
    logging.basicConfig(stream=sys.stderr)
    logging.info("Processing task")
    ```
  </Tab>

  <Tab title="Node.js">
    ```javascript theme={null}
    console.error("Debug info");

    // Or use process.stderr directly
    process.stderr.write("Processing task\n");
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    import "os"
    fmt.Fprintln(os.Stderr, "Debug info")

    // Or use log package (defaults to stderr)
    log.Println("Processing task")
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    eprintln!("Debug info");

    // Or use tracing/log crates configured for stderr
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    System.err.println("Debug info");

    // Or use a logger configured for stderr
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    Console.Error.WriteLine("Debug info");
    ```
  </Tab>

  <Tab title="C++">
    ```cpp theme={null}
    std::cerr << "Debug info" << std::endl;
    ```
  </Tab>
</Tabs>

## Minimal Example

Here's the simplest possible handler in Python:

```python theme={null}
import json
import sys

for line in sys.stdin:
    task = json.loads(line)
    result = {"echo": task["task_data"]}
    print(json.dumps({"status": "success", "result": result}), flush=True)
```

## Dockerfile

Handlers must be packaged as Docker images. Make sure to install the SDK from the package manager:

```dockerfile theme={null}
FROM python:3.11-slim
RUN pip install --no-cache-dir taskdaemon
COPY handler.py /app/
WORKDIR /app
CMD ["python", "-u", "handler.py"]
```

<Warning>
  Always use unbuffered output (`-u` flag in Python, `flush=True`, etc.) to ensure responses are sent immediately.
</Warning>

## Configuration

Register handlers in `handlers.toml`:

```toml theme={null}
[handlers.myhandler]
image = "my-handler:latest"
instances = 4
timeout = 30
handler_selection = "round-robin"
```

See [handlers.toml configuration](/configuration/handlers-toml) for all options.
