---
title: "Troubleshooting"
description: "The errors you're most likely to meet, what they mean, and how to fix them."
type: "docs-page"
url: "https://tatolab.com/docs/reference/troubleshooting"
site: "Tatolab"
section: "Reference"
---

# Troubleshooting

> The errors you're most likely to meet, what they mean, and how to fix them.

Tatolab's errors try to name the problem and the fix. When one isn't clear, find it here. For a stream that runs but misbehaves, start with [Inspect a running stream](https://tatolab.com/docs/run/inspect).

## The runtime

**`error: no runtime is running on this machine: nothing answers at …`**

The runtime isn't running, so no command can reach it.

- **Mac:** open Tatolab and check that **Run Tatolab** is on in its menu.
- **Linux:** `systemctl --user start tatolabd`, and `systemctl --user status tatolabd` to see why it stopped.
- **Source build:** run `tatolabd` in a terminal of its own.

If the message goes on to say the machine's runtime is held by another user, that runtime serves their account, not yours. A machine runs one runtime, for one user, at a time.

**`another runtime holds this machine: …`**

A `tatolabd` is already running. The message gives its user, process ID and executable. Use that one, or stop it before starting another. A common cause is a `tatolabd` started in a terminal while the installed service is also running.

**`the machine runtime lock cannot be taken: /Library/Application Support/Tatolab … does not exist`** (macOS)

The Tatolab app creates that folder on its first launch. Open the app once. Without the app, for a source build, run the `sudo` commands the message prints, once.

**`DisplayWindow: no window — running degraded (frames drained, nothing shown)`**

The window couldn't open, usually because the runtime has no display. The rest of the stream keeps running. On Linux, the runtime picks up your desktop's display when it starts. The installed service restarts itself when you log in to a desktop; a `tatolabd` you started by hand needs restarting from a terminal inside the desktop session. On a machine with no desktop, leave windows out and look at frames with `tatolab exchange`.

**`No usable Vulkan driver (ICD) was found`** or **`No Vulkan loader library could be opened`** (Linux)

The GPU driver's Vulkan support is missing. Install your GPU's Vulkan driver (the proprietary NVIDIA driver, or `mesa-vulkan-drivers` for AMD and Intel) and the Vulkan loader (`libvulkan1`), then check that `vulkaninfo --summary` works.

## Loading a stream

**`no stream.py in …`**

`tatolab run` and `tatolab dev` look for `stream.py` in the current folder only, never in parent folders. Run them from the project folder, point at it with `--dir`, or name the file with `-f`.

**`… has no .venv/bin/python; run uv sync in …`**

The runtime runs your stream with the project's own environment, in `<project>/.venv`. Run `uv sync` in the project folder. If you pointed at another folder, check `--dir`.

**`… cannot import tatolab.stream: add tatolab-stream to the project's dependencies`**

The project doesn't depend on `tatolab-stream`. Run `uv add tatolab-stream`.

**`node … is defined in the entry file`**

Each Python node runs in its own process, which imports the class by its module path, and `stream.py` can't be imported that way. Move the class into a module of its own, such as `nodes/detector.py`, and import it into `stream.py`. The error suggests a module name.

The same goes for a node class defined inside a function (`… is defined inside a function`): move it to the top level of a module. If the function's arguments shaped the class, pass them as the node's settings instead.

**`tatolab dev: no stream is loaded — fix it and save again`**

The stream failed to load, and no earlier version was running to fall back on. The error is printed just above. Fix it and save.

**`no stream.py in …, only an app.py`**

The current convention is `stream.py` with a `@stream` function. Older projects and the repository's examples use `app.py` with `setup(rt)`, which is being replaced. To run a `@stream` function from another file, name it with `-f`. See [Examples](https://tatolab.com/docs/examples) for the examples' streams written the current way.

**`a stream named … is already loaded in this runtime, from …`**

Stream names are unique on a machine, and the message names the project using this one. Stop or remove that stream, or load yours under another name with `--name`.

**`the stream name … is held by a kept stream from …`** (or by a stopped or failed one)

A kept stream holds its name even while it's stopped or failed, which is what you meet when you run `tatolab dev` in a project whose stream you kept. Remove the kept one while you edit (`tatolab rm <name>`, then `tatolab run -d` again when you're done), or load yours with `--name`.

## Nodes

**`input port 'video' must declare a delivery_profile`**

Every `@node.input` needs `delivery_profile="newest"` or `"ordered"`. See [delivery profiles](https://tatolab.com/docs/build/nodes#delivery-profiles).

**`… declares the port name 'video' more than once`**

Inputs and outputs share one set of names on a node. Rename one, such as `video` in and `processed_video` out.

**`… declares no input ports, so it must declare an execution mode`**

A source has nothing to react to. Use `@node(execution="continuous", interval_ms=...)` to run on a timer, or `execution="manual"` to drive it yourself.

**A node fails to start with `unexpected keyword argument` or `unknown field`**

A misspelled key in the `config` you passed to `stream_builder.add`, or a key the node doesn't have. The message names it. Built-in nodes and dataclass settings refuse unknown keys this way; pydantic models ignore them unless you set `extra="forbid"`, and `TypedDict` settings take anything, so a typo there goes unnoticed. For a `TypedDict`, build the dict by calling it, as in `config=DetectorConfig(threshold=0.5)`, and your type checker catches the typo.

**``node `…` has no input port `…` ``**

The stream connects to a port the node doesn't have. The message lists the ports it does have. Check the spelling, and check that no output method has the same name as an input method: Python keeps only the last method of a name, so the input disappears.

**A GLSL error**

The message gives the line number within your `effect` string. Check for a missing semicolon, and that `effect` returns a `vec4`.

## Devices

**No camera, or the wrong one**

`CameraSource` uses the first camera it finds unless `device_id` names one: a path like `/dev/video2` on Linux (list them with `v4l2-ctl --list-devices`), or a camera's unique ID on macOS. A named camera that can't be opened is refused by name. With no camera at all, use `TestPatternSource` (`tatolab new … --test-pattern`).

**No picture on macOS**

The first run asks for camera access without blocking the stream: frames start once you allow it. If you refused, allow it in **System Settings → Privacy & Security → Camera**. The prompt and the setting name the app that started the runtime: Tatolab, or your terminal app for a source build.

**No sound, or a recording that stays empty**

Check the microphone permission on macOS, and the device name if you set `device_id`. An `Mp4Sink` waits until every track has sent its first data before it writes anything. Until then it logs `waiting to write moov` once a second, with the tracks it's still waiting for, which is how a track that never starts shows up.

**A `VirtualCameraSink` can't create its camera**

Run `tatolab enable-virtual-camera` once. It needs the v4l2loopback module for your kernel (`v4l2loopback-dkms` on Debian and Ubuntu). Until then, the sink presents a PipeWire camera instead. See [Virtual camera](https://tatolab.com/docs/build/virtual-camera).

**`the encoder session could not be minted; every later frame is discarded`**

The GPU or driver has no hardware video encoder Tatolab can use for that codec. Try the other codec (`H265Encoder` instead of `H264Encoder`, or the reverse), and check your driver's Vulkan Video support on Linux.

**A USB webcam runs at a few frames per second on Linux**

`CameraSource` reads uncompressed video only, and picks the largest size within `max_width` and `max_height`. Most USB 2.0 webcams send uncompressed HD at 5 to 10 frames per second; their faster HD modes are MJPEG, which isn't read yet. Lower the limits (`"max_width": 640, "max_height": 480` usually runs at 30), or use a USB 3 camera. `v4l2-ctl --list-formats-ext` lists what your camera offers.

## Inspecting

**`no tappable channel named …`**

`tatolab tap`, `tatolab exchange --channel` and an agent's `tap` can only sample an output that something reads: a link in its own stream, or another stream reading it. Connect something to the output, or sample one that already has a reader. Exposure doesn't matter here.

**`bash: tatolab: command not found` over ssh**

A command run over `ssh` gets a non-interactive shell, and on Debian and Ubuntu that shell doesn't have `~/.local/bin` on its `PATH`. Give the path instead: `ssh door-box .local/bin/tatolab streams`.

## Logs

**`tatolab logs --list` shows nothing**

Each stream's log files live in its project's `.streamlib/` folder, so run `tatolab logs --list` in the project folder. For a running stream, `tatolab logs --stream <name>` works from anywhere.

## Still stuck?

Open an issue on [GitHub](https://github.com/tatolab/streamlib/issues) with the command you ran, the full error, and `tatolab --version`, or email [hello@tatolab.com](mailto:hello@tatolab.com).
