DEV Community

octave Nkurunziza
octave Nkurunziza

Posted on Originally published at blog.hubfly.space

Build failed? How to read the build log and fix it fast

Originally published on the Hubfly space blog.

TL;DR

  • What you get: a 3-step way to read any failed build, plus the exact fix for 5 common errors (wrong branch, wrong folder, bad base image, npm ci with no lockfile, and a deploy that fails after a good build).
  • Who it's for: anyone deploying from Git who just saw a red "Failed" badge and a wall of log text.
  • Time needed: about 10 minutes to read; each fix takes 1–2 minutes plus a rebuild of a few seconds.

You pushed your code, clicked deploy, and got a red Failed badge. The log is 60 lines long and the last line just says exit status 1. That last line is almost never the answer. The real cause is usually one line, a little higher up, and once you know where to look you can fix most failed builds in a couple of minutes.

This guide shows you how to find that line and fix it. You'll learn which part of the build stopped, which line holds the cause, and the fix for five common errors. You can follow along on Hubfly space's free tier (360 hours a month, no card), and builds themselves are free.

Try it on Hubfly space →

Step 1: Find out where the build stopped

Open your project, go to the Builds tab, and click the failed build. Each build has its own page with a three-part pipeline at the top: 1. Triggered, 2. Build Phase and 3. Deploy.

The build page shows which part of the pipeline stopped.

Read the pipeline first, because it tells you which kind of problem you have:

Where it stopped What it usually means Where to look
Deploy says "Deployment did not start", and the log ends with an ERROR or Build failed line Your code, your settings or your Dockerfile The build log
Deploy shows its own red message, the log ends with Hubcell build successful. and the box shows Exit code: 0 The build worked; something blocked the deploy The message under Deploy

Why this matters: Build Phase says "Build compilation failed" and the red box says Compilation Error in both cases. If you only read that box, you can spend an hour "fixing" code that was never broken (see error 5 below).

Step 2: Read the log from the first error, not the last line

Scroll down to Remote Build Terminal. Each line starts with a timestamp, and the build runs top to bottom in this order:

  1. Clone: git clone and Checking out ref: <branch>
  2. Detect: Using working directory: <folder> and, if you gave one, Using custom Dockerfile from build request.
  3. Build: Step 1/7: FROM …, Step 2/7: … and so on, one line per Dockerfile instruction
  4. Finish: Hubcell build successful., or ERROR: … exit status 1

To find the cause:

  • Find the last Step N/M: line. That's the instruction that failed.
  • Read the first line that says error, ERROR or failed after it. That's the cause.
  • Ignore the final exit status 1. It only tells you the build stopped.

If the log looks stuck while a build is running, refresh the page. The full log loads again.

Tip: the Copy button copies the whole log. Paste the part from the last Step line down into your notes or an issue. That's usually all anyone needs to help you.

Step 3: Fix it with Edit & retry

On a failed build page, click Edit & retry. The create form opens with every setting from that build already filled in, and a banner that says "Restored from a failed build. Fix the settings and deploy again." Change the one thing that broke, click through to Review, and click Start Build.

Why this matters: you don't have to retype the repository, branch, folder, commands or Dockerfile. You change one field, so you know exactly what fixed it.

Now, five errors worth knowing by sight.

Error 1: "pathspec 'main' did not match any file(s) known to git"

Checking out ref: main
error: pathspec 'main' did not match any file(s) known to git
ERROR: failed to checkout ref main: exit status 1
Enter fullscreen mode Exit fullscreen mode

The build stops right after the clone when the branch doesn't exist.

What it means: the clone worked, but the branch you asked for doesn't exist. The Branch field starts as main, and plenty of repositories still use master, develop or production.

Fix: check the branch name on GitHub (the branch dropdown above the file list), then click Edit & retry and set Branch to that exact name. Branch names are case-sensitive.

Error 2: "failed to auto-detect build config: unsupported runtime: unknown"

Using working directory: apps/web-api
ERROR: failed to auto-detect build config: unsupported runtime: unknown
Enter fullscreen mode Exit fullscreen mode

A typo in the folder name shows up as an

What it means: the builder looked in the folder you gave it and found nothing it could detect a runtime from. The most common cause is not your code at all. It's a typo in Repository directory: the folder doesn't exist, so it looks empty.

Fix: check the line just above the error, Using working directory: …. Compare it with the real path in your repository, character by character. For a monorepo, the path is relative to the repository root, like services/api. If the folder is right and the error stays, paste a Custom Dockerfile, or untick Automatically detect the build and choose the runtime yourself.

Error 3: "MANIFEST_UNKNOWN: manifest unknown; unknown tag"

Step 1/7: FROM node:20-alpine3.10
Build failed: failed to pull base image "node:20-alpine3.10": ... MANIFEST_UNKNOWN: manifest unknown; unknown tag=20-alpine3.10
Enter fullscreen mode Exit fullscreen mode

What it means: the image in your FROM line doesn't exist on Docker Hub. It's usually a tag that mixes two versions that were never published together, or a tag that was removed.

Fix: look up the tag on the image's Docker Hub page, under Tags, and use one that's listed. For Node, use a supported LTS tag such as node:22-alpine (Node 20 stopped getting security fixes in April 2026). Pin a major version (22), not latest, so a new release can't change your build overnight.

Error 4: "The npm ci command can only install with an existing package-lock.json"

Step 3/7: COPY package.json ./
Step 4/7: RUN npm ci --omit=dev
npm error code EUSAGE
npm error The `npm ci` command can only install with an existing package-lock.json or
npm error npm-shrinkwrap.json with lockfileVersion >= 1. ...
Build failed: build command failed: exit status 1
Enter fullscreen mode Exit fullscreen mode

Read the first

What it means: npm ci installs exactly what's in your lockfile, so it refuses to run without one. Here the lockfile is in the repository, but the Dockerfile only copied package.json into the image. Look one step up: Step 3/7: COPY package.json ./.

Fix: copy the lockfile too. This is the whole working Dockerfile for a small Express app, on a supported Node version and running as the image's non-root node user:

FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY server.js ./
USER node
EXPOSE 5000
CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

While you're in the log, read the npm audit summary under the install step. If it lists high or critical vulnerabilities, update those packages before you ship.

If your repository has no lockfile, run npm install on your machine and commit the package-lock.json it creates. That's better than switching to npm install in the Dockerfile, because the lockfile is what makes every build install the same versions.

The same pattern breaks other stacks: yarn install --immutable (--frozen-lockfile on Yarn 1) needs yarn.lock, pnpm install --frozen-lockfile needs pnpm-lock.yaml, and bun install --frozen-lockfile needs bun.lock. Copy the lockfile in the same COPY line as the manifest.

Error 5: "Compilation Error", but the build worked

This one is easy to misread. The badge says Failed, the red box says Compilation Error, and you go hunting for a bug. Then you scroll to the bottom of the log:

Step 8/8: CMD ["node", "server.js"]
Successfully built … (took 10s)
Hubcell build successful.
Enter fullscreen mode Exit fullscreen mode

Build Phase is red, but the reason is under Deploy, and the log ends with

What it means: your code compiled fine. The container couldn't start for a reason outside your code. The real message sits under 3. Deploy in the pipeline. A common one on the free tier:

Monthly free compute hours are used up for this account. Choose a dedicated tier or wait for the allowance to reset next month.

The box below shows Exit code: 0, which is your other clue: nothing in the build failed.

Fix: read the message under Deploy and act on that, not on your code. For the free-hours message, either wait for the monthly reset or choose a dedicated tier, as the message says (Edit & retry, then the Resources step). A Dedicated container defaults to 1 vCPU and 1 GB; at 0.5 vCPU, 512 MB and 4 GB of disk with Auto Sleep off, it costs about $2.23 a month:

  • CPU: 0.5 vCPU × $0.004 × 730 h = $1.46
  • RAM: 0.5 GB × $0.002 × 730 h = $0.73
  • Disk: 4 GB × $0.00001536 × 730 h = $0.04

Total: about $2.23 a month, and you can set a monthly budget cap on the project. The cap is a hard stop: you get email warnings first, and when this month's spend (UTC) reaches the cap, the project's running containers and managed services stop and new deploys are blocked.

Good news on speed: once a build has run, the next one reuses unchanged layers (---> Using cache). A fixed build took about 10 seconds, and rebuilding with nothing changed took 2, so trying a fix costs you almost nothing.

What can go wrong

  1. You fix the Dockerfile in Git, but the build doesn't change. If you pasted a Custom Dockerfile in the form, the build uses it instead of the Dockerfile in your folder (the log says Using custom Dockerfile from build request.). Clear that field, or update it there.
  2. Your install, build and run commands are ignored. When a Dockerfile is used, the log says submitted install/setup/build/run phases are ignored because a Dockerfile was provided. Put those steps in the Dockerfile itself (RUN for install and build, CMD for run). With auto-detect turned off, the form still asks for a run command; fill it in, but the Dockerfile's CMD is what runs.
  3. A heavy build runs out of time or memory. The default build timeout is 900 seconds, and the builder has 2 CPUs and 4 GB of memory. If a frontend build dies with JavaScript heap out of memory or hits the timeout, cut what gets built: install dev dependencies only in a build stage and copy just the output into the final image, add a .dockerignore for node_modules, .git and test data, so the build context stays small.

The takeaway

A failed build is a search for one line. Check where the pipeline stopped, read the first error after the last Step line, and ignore the final exit status 1. If the log ends with build successful, stop looking at your code and read the message under Deploy. Then change one thing with Edit & retry, and let the cache make the retry nearly instant.

Read next: Fix a 502 Bad Gateway in Docker by binding to 0.0.0.0, for when the build passes but the app won't answer.

Go further on Hubfly space

  • Deploy on every push: connect GitHub with the GitHub App so each push builds and deploys, or deploy only after your CI passes. See Builds and how to deploy on every push with GitHub Actions and a webhook.
  • Run a database next to your app: add Postgres or Redis in the same project and connect over the private network.
  • Cap your spend: set a monthly budget on the project in Project Settings → General & Budget, so a Dedicated container can't surprise you.

Deploy this

Have an app with a red build badge right now? Open it in Hubfly space, click the failed build, and follow the three steps above. If you're starting fresh, create a free project, connect your repository, and use the Dockerfile from Error 4 as a starting point for a small Node server (add a build stage if your app compiles TypeScript or a frontend).

Top comments (0)