Build failure notifications show only the failing command, not the build output
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 37.4k
- Forks
- 3k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 73
Description
Summary
Build failure notifications carry only the failing command, never the compiler/builder output — even though the output was captured and is sitting on the error object. The information exists; it just isn't read.
This makes an out-of-hours alert unactionable: you learn that the build broke, not what broke, so triage always requires opening the build log.
What the notification looks like today
A staging deploy that failed on a TypeScript error produced this in Slack:
Command execution failed: Command failed: (set -e;
set -e
{
echo ...
App Name: <app>-<hash>
Build Compose
Detected: 0 mounts
Command: docker compose -p <app>-<hash> -f ./docker-compose.staging.yml up -d --build --remove-orphans
The actual cause was a single line the builder printed:
src/<path>/<file>.ts:319:43 - error TS2345:
Argument of type 'number | null' is not assignable to parameter of type 'number'.
That line never reaches the notification.
Root cause
Three things compose into the empty message:
getBuildComposeCommandappends2>&1to the docker command, so the builder's stderr is folded into stdout.- Node's
execbuilds its error message asCommand failed: <cmd>plus the child's stderr. With stderr emptied by step 1, there is nothing to append — the message degenerates to the command line. - **
execAsyncthrowsExecError(\Command execution failed: ${error.message}`, { command, stdout, stderr, exitCode, ... })** —packages/server/src/utils/process/execAsync.ts. The real output survives onerror.stdout`.
Then the notification reads only the message:
// packages/server/src/services/compose.ts:349 (v0.30.7)
errorMessage: error?.message || "Error building",
packages/server/src/services/application.ts:271 has the same shape.
The Slack/Telegram templates in packages/server/src/utils/notifications/build-error.ts already render errorMessage in a code block without truncating — so the presentation layer is fine. Only the value handed to it is empty.
Suggested fix
Prefer the captured output over the message when the error is an ExecError, tailing it so a long build log doesn't overflow the notification:
const tail = (s?: string, lines = 30) =>
s?.trim().split("\n").slice(-lines).join("\n") || "";
const detail = tail(error?.stderr) || tail(error?.stdout);
errorMessage: [error?.message, detail].filter(Boolean).join("\n\n") || "Error building",
Reading stderr first keeps the fix correct for commands built without the 2>&1; falling back to stdout covers the compose path.
An alternative would be dropping 2>&1 from the built command so Node populates the message itself, but that changes what gets streamed to the deployment log — the read-side fix above seems lower risk.
Environment
- Observed on Dokploy v0.29.8, self-hosted; the lines quoted above are unchanged in v0.30.7
- Compose deployment, GitHub source, autodeploy on push
- Notification provider: Slack (the same value feeds every provider, so this is not Slack-specific)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/server/src/utils/process/execAsync.ts, then trace the errorMessage assignments at packages/server/src/services/compose.ts:349 and packages/server/src/services/application.ts:271. Check the existing build-error notification templates and verify that captured build output, including compose stdout, is included and limited to a useful tail without breaking other providers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, node.js, typescript
- Domain
- backend, devops
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100