Why a deploy failed: 7 common causes and fixes
· updated
Almost every rejected deploy comes down to one of seven causes, and in each case Tuqo tells you what exactly is wrong with your file set instead of a generic “index.html required”. Below are those seven cases, what the error says and what to do. If the site did publish but behaves wrongly, that is a different story: see what AI agents break in sites.
A note on language. The messages below are quoted in English. Errors in the panel and
the REST API come in the language of the request: English in the English panel or with
Accept-Language: en, Russian otherwise. Over MCP they are always in English.
Build errors and build logs follow the language of the site owner’s account, set in
Profile settings under Email and notification language.
1. Nested folder: index.html is not at the root
The most common case in production. Someone packed the whole folder, so everything sits
one level deeper: my-site/index.html instead of index.html.
One extra level in an archive is fine. If a tar.gz or zip has exactly one
top-level folder, Tuqo steps into it and publishes its contents. An archive made with
tar czf site.tar.gz my-site publishes as is.
The error appears when there are two levels, my-site/dist/index.html, and the middle
folder holds nothing else:
The site files are in the nested folder “dist”, but they must be at the root. Pack the CONTENTS of the folder, not the folder itself:
tar -czf site.tar.gz -C ./dist .— or just drag the folder into the panel, which handles this automatically.
Files sent one by one are not flattened. That is how agents publish over MCP, REST and
the CLI: paths go as they are, and my-site/index.html does not become the home page:
all files are inside the nested folder “my-site”, but index.html must be in the root. Drag the “my-site” folder itself (its contents become the root) or remove the extra folder level.
If there are several top-level folders and the root has neither index.html nor
package.json, Tuqo lists what actually arrived. One look at the list shows that the
wrong thing was packed.
2. index.htm instead of index.html
A site opens at /, and that address serves exactly index.html. A file with another name
or in another letter case will not become the home page:
the home page must be named exactly index.html, but the set has “index.htm”. Rename the file and upload again.
If the root has other HTML pages but no home page, the message says that too: it lists the
pages it found (about.html, contact.html) and reminds you that the site opens with
index.html. Rename your home page.
3. One file gzipped instead of a folder archive
gzip index.html produces index.html.gz. Its magic bytes say gzip, so the upload check
lets it through, but there is no tar structure inside:
Could not unpack the archive: there is no tar structure inside. It looks like a single file was compressed instead of a folder. Build the archive from the CONTENTS of the site folder:
tar -czf site.tar.gz -C ./folder .— or just drag the folder into the panel. A .zip works too.
Two formats are accepted: tar.gz and zip. Anything else is rejected right away with
“Archive not recognized: tar.gz and zip are accepted.” Zip is the safer choice, by the way:
Windows and most AI tools produce it by default.
4. The build produced nothing
If the archive root has a package.json, Tuqo treats the upload as a build project and
builds it on Node 24: npm ci when there is a lock file (otherwise npm install), then
npm run build. Three typical failures:
-
package.jsonhas nobuildscript. The build fails onnpm run build, and the log shows npm’s missing script error. Add the script. For a repository connected with Git CD, you can also set your own build command in the Git CD settings. -
The output is not where Tuqo looks for it.
The build succeeded, but no folder with the finished site was found. We look for dist, dist/client, build, build/client, out, .vitepress/dist, .output/public, _site, site, storybook-static, public. If your stack puts the output elsewhere, set the output directory in the Git CD settings or upload the built folder.
-
The build succeeded, but there is no home page. This happens with server-side rendering (SSR) projects: Next.js without
output: 'export', Nuxt withoutnuxi generate, new Lovable projects on TanStack Start. Their HTML is rendered by a server, and the output folder holds only scripts and styles. Tuqo serves ready-made files and does not run a server, so it does not publish such a build (the previous version of the site keeps working) and tells you how to get a static build for your stack:The build succeeded, but the output directory “.output/public” has no index.html (the site’s home page), so the site would open with a 404 error. Publishing was stopped…
For TanStack Start it is enough to turn on SPA mode:
tanstackStart: { spa: { enabled: true } }invite.config.ts. Tuqo picks up the_shell.htmlhome page it generates when Tuqo builds the project; if you build locally, rename_shell.htmltoindex.htmlbefore uploading.
The reverse case: you publish a built site as files, and a package.json slipped into
the set. Then you get “package.json found in the root: this is a build project. For static
files remove it; to build, use deploy_site (an archive with the sources)”.
In the panel, the site’s Deploys tab shows the failed deploy with its status and the error text from the end of the build log.
5. You hit a limit
Each publishing path has its own ceiling:
- A folder dropped into the panel: up to 2000 files and about 36 MB. The panel tells you
the total size and suggests packing a tar.gz or zip (up to 50 MB) or using the CLI,
npx @tuqo/cli deploy. - An archive in the panel, over REST or with MCP
deploy_site: 50 MB. A bigger one is rejected: the panel and the REST API say “The source archive exceeds 50 MB.”, and MCP returns the same message. - The CLI: up to 2000 files, up to 50 MB per file, in total within your plan’s storage. There is no request body size limit: files go up one by one.
A separate common story is “too many files”: node_modules or the source folder ended up in
the archive. Publish the build output, not the whole project. Heavy sites with photos go
through the CLI; see the guide on
updating a media-heavy site without re-uploads.
6. Service files and __tuqo/
Some paths are never published, and that is not an error: they are simply dropped from the
set. These are .git, .hg, .svn, .bzr, .env (and .env.local, .env.production),
.ssh, .aws, .gnupg, .docker, .kube, .config, .netrc, .npmrc, .pypirc,
.htpasswd, .htaccess, .DS_Store. Every segment of the path is checked, case-insensitively,
so frontend/.env.local is dropped as well.
The .well-known folder is published: that is where assetlinks.json (opening links in an
Android app), apple-app-site-association (the same for iOS, served as JSON) and
security.txt live. Only acme-challenge/ and pki-validation/ inside it are not
published: they are used to issue SSL certificates, and Tuqo issues certificates itself. A
Drop draft only accepts files with an extension, so apple-app-site-association is not
published there: claim the draft and upload it with a regular deploy.
The __tuqo/ prefix is separate: the platform reserves it for its own pages (the password
login screen, file delivery). A site file under that path would never be served, so it is
not published at all.
If nothing but service files is left in the set, you will see the missing index.html
error. Agents get the list of dropped files in the skipped_paths field; there is no need
to send those files again.
7. PHP files
Tuqo is a static host: PHP, like any server code, does not run here. A form with
action="send.php" will not send anything, and the .php file itself would be served as a plain
file, along with the mail password or bot token written in it. So .php (as well as .php5,
.phtml, .phar) is neither published nor served, including on sites published earlier.
On the Free plan a site with PHP files is not published at all, with a message like:
The site contains PHP files (forms/contact.php). Tuqo publishes static files only and does not run PHP: a PHP form would not send anything, and its code with passwords and tokens would be visible to anyone.
On paid plans the site is published without them, and the skipped files show up in the
notification and in the skipped_paths field.
What to do: remove the .php files (in ready-made templates this is usually a forms/ folder or
send.php, mail.php) and use Tuqo Forms: the project’s Forms section has a ready
HTML form, and submissions arrive in the dashboard and by email. If the PHP files held passwords
or tokens, change them.
FAQ
How do I build the archive correctly?
tar -czf site.tar.gz -C ./dist . — the dot at the end means “the contents of the folder”.
Or run zip -r site.zip . from inside the site folder. To check, run
tar -tzf site.tar.gz | head: the list should show ./index.html, not dist/index.html.
The deploy succeeded, but the site returns 404
Every publishing path requires index.html at the root, and Tuqo does not publish a build
without a home page. If the site still returns 404, it is turned off in its settings, or the
live build was published before October 6, 2026: deploy again. A 404 on page refresh in an
SPA with client-side routing is a different problem with its own fix; see
SPA 404 on refresh.
Where can I see why a build failed?
The site’s Deploys tab → the failed deploy: its status and the error text (the
last lines of the log). Over MCP, get_logs(deploy_id) returns the same.
Can I roll back if a new version breaks?
Yes, if your plan keeps more than one version: the Deploys tab → “Roll back to this”. Details are in the guide on rolling back to a previous version.