vibeapp.win

What broke

Start failed where Stop worked: an ø in the folder name and a dev override

Portal's Stop button worked for one app, but Start failed for two separate reasons: Compose's bake builder breaks on an ø in the folder path, and Compose quietly read a dev-only override file. Neither case was handled in the code Claude wrote. The fix turns bake off for non-ASCII paths and adds an optional config field that names the compose files.

The short version

What went wrongStart failed on one app while Stop worked: Compose's bake builder broke on an ø in the folder path, and Compose silently read a dev-only override file that started a placeholder container.

How we found itHenning tried Start after Stop went fine. The build failed, and once that was out of the way only a placeholder container came up. The lifecycle had only been tested halfway.

The rule nowTest both Start and Stop on an app with a non-ASCII path before calling a Compose feature done. Turn bake off for such paths and use a config-only composeFiler list when the override is dev-only.

This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes

Portal runs the app park. For each app, its Status tab has Start and Stop buttons. Portal itself runs in a container, so it doesn’t run Docker directly. It puts a job in a queue, and a watcher process on the host picks the job up and runs docker compose in the app’s folder. Start means up -d --build, so it builds and replaces the container in one step. Stop means down.

Claude wrote all of it: the queue, the watcher, the job planner. On one app, Stop went fine. Start did not. The commit that fixed it, from 7 October 2026, opens like this (translated from Norwegian):

Stop went fine, but Start failed. Two faults, both outside the watcher.

“Outside the watcher” is accurate in a narrow sense. The two faults sat in what Docker Compose does with the folder it’s given. The code Claude wrote had no handling for either case. Stop working proved very little, because Stop never builds anything.

Fault one: an ø in the folder name

The app’s folder name contains the Norwegian letter ø. Every Start that included a build failed with the same message:

x-docker-expose-session-sharedkey contains value with non-printable ASCII

This came from Compose’s newer bake builder, not from Portal. The comment Claude added to the watcher explains it: the bake builder sends the folder path in a gRPC header, and that header only accepts ASCII. Any folder with æ, ø or å (or any other character outside plain printable ASCII) fails every time. Compose’s own older build path doesn’t have this problem.

So the build never got started. Because a failed build makes Compose stop before it touches the container, Start ended in an error with nothing changed.

Fault two: without a build, a placeholder came up

Without a build, Start had a second problem. The changelog describes it from the user’s side: it looked as if the app started, but the container that came up was only a placeholder from the development setup. It wrote one line and exited.

The cause was the override file. When Compose runs with no -f flags, it reads docker-compose.yml and also docker-compose.override.yml if one exists. In this app, the override file was the dev setup, and it turns the app container into a placeholder. Compose did what it’s documented to do. Portal had no way to tell it to skip that file.

The fix

There are two changes, both narrow.

Bake off for non-ASCII paths. Before the watcher starts docker compose, it checks the app’s folder path. If the path has any character outside printable ASCII, the watcher sets COMPOSE_BAKE=false for that one process, so Compose uses its own builder. Every other app builds the same way as before. We kept this to the cases that need it on purpose, so the change can’t affect apps that already worked.

An optional composeFiler field in the app config. This is a list of compose file names. When it’s set, Portal passes each name to Compose as -f, ahead of the verb, for Start and Stop alike. The affected app now has just docker-compose.yml in that list, so the dev override is never read. When the field is empty, Compose picks its files itself, override included, exactly as before. Config docs now say when to set it: when the override file is a dev setup.

Because this field ends up on a command line, it got the same treatment as the existing setting for which subfolder the compose file lives in. It comes only from the config file, never from the request that asks for a Start. The planner accepts at most five entries. Each one must be a bare file name ending in .yml or .yaml, made of letters, digits, dots, underscores and hyphens, and must not start with a dot. Anything else gets the request rejected.

New tests cover both sides. One checks that a configured list becomes -f docker-compose.yml in front of up -d --build, and that an app without the field still gets a plain down. The other feeds the planner bad entries and expects every one to be rejected: a relative path going up a level, an absolute Windows path, --project-directory, -f, two names separated by a space, a hidden file, a .txt file, and a single string where a list was expected.

The changelog entry for users says nothing needs doing for this app, since it’s already configured. Folders with æ, ø or å can now be built from the Status tab. Any other app whose override file is dev-only can get the same field.

What we take from it

Neither fault was exotic. The ø in the folder name is an ordinary Norwegian letter, and the override file is a standard Compose feature. Stop working did not show that Start worked, because Stop never builds anything.

The rule: test the whole lifecycle, both Start and Stop, on an app whose path has non-ASCII characters before calling a Compose-driven feature done. The watcher turns off bake for such paths. Any app whose override file is dev-only gets an explicit composeFiler list. That list holds file names only, taken from the config.