Troubleshooting

When something breaks

Sorted by what you see. Start with owfeed doctor or owlab doctor — both print numbered findings that say what failed, the command that failed, and what to do.

Installing on a router

What you seeCause and fix
UNTRUSTED signature after a firmware upgradeThe key is gone. /etc/apk/keys/*.pem does not survive sysupgrade on its own. Re-add the key, then list it and your repository file in /lib/upgrade/keep.d/<feed>, which sysupgrade reads alongside /etc/sysupgrade.conf.
apk asks for --allow-untrustedThe index is not signed by any key the router trusts. Check the key file is in /etc/apk/keys/ and that you are pointing at the right feed. Do not pass the flag — it disables the check you installed the key for.
Nothing downloads, HTTPS failsA stock image cannot fetch over HTTPS. apk add ca-bundle libustream-mbedtls first.
The feed URL 404s or returns nothingapk does not follow redirects (openwrt#17180). If the feed moved, put the final URL in the repository file. For apk the URL is the index file; for opkg it is the directory.
Your package never upgradesIt was installed from a file. apk add ./file.apk pins the content hash in /etc/apk/world, and that survives sysupgrade. Remove the package, add the repository, install by name.
Packages vanished after Attended SysupgradeExpected. owut forwards no custom repositories and the server's repository_allow_list is empty by default. Re-add the feed and reinstall.
breaks: world[...] on every installThe rootfs and the feed are different point releases. Pin both to the same exact release.

Building and packaging

What you seeCause and fix
apk rejects the package as uninstallablearch: all. apk requires noarch. Use LUCI_PKGARCH:=all in a Makefile and let the build translate it, or write arch: noarch in owfeed.yml.
The key is refusedA PKCS#8 PEM. openssl genpkey -algorithm EC writes the wrong shape; only SEC1 works. Generate with owfeed keygen.
The version is rejectedAfter ~ apk reads a commit hash, so only hex digits are allowed. Write 1.0_beta1, not 1.0~beta. -r<n> goes last.
The index does not match the packagesYou indexed before signing. Signing appends bytes. Run sign, then index.
Translations do not appearLuCI reads compiled .lmo and ignores .po. Point i18n.from: at your catalogues and owfeed compiles them.
A user's settings are replaced on every upgradeThe file is shipped but not in conffiles:. Declare every /etc/config/* the package installs.
Works unminified, breaks after a real buildluci.mk minifies JS and CSS; owlab sync does not. Run owlab build before releasing.
A package is missing from one release lineowfeed doctor reads the tree and cannot see what was never written. Compare the built tree against your config — that is what a feed's own tree check is for.

Publishing a feed

What you seeCause and fix
$OWFEED_SIGN_KEY is emptyThe secret is missing, or the job started before it existed. Note that a calling workflow cannot read an environment secret — signing secrets have to be repository or organization secrets.
OWF513: a published version has different contentsA package changed without a version bump, or the build stopped being reproducible. Do not publish over it. Bump the revision.
OWF514: the live site lost .nojekyllactions/upload-pages-artifact drops dotfiles from v4. Set include-hidden-files: true, or Pages runs Jekyll over your feed and deletes every path starting with a dot or underscore.
Pages deploy fails on permissionsactions/deploy-pages needs actions: read, which a permissions: block silently withholds by naming other scopes.
owfeed doctor passes but smoke failsThe tree is coherent and a router still will not take it. Read the container output: usually a dependency that does not resolve on a stock image, or a file installed where nothing looks.
A documented install URL 404sYour README drifted from the feed. Generate it: owfeed install-snippet. doctor compares the two.
The build fails on a stale lockUpstream added or removed an architecture. Run owfeed lock --update and read the diff before committing it.

Test routers (owlab)

What you seeCause and fix
LuCI answers but nothing worksprocd jails services and a container cannot build a jail. Use fidelity: vm for anything that needs a real kernel or a jailed service.
A page returns 403 on a healthy routerAn unauthenticated request to /cgi-bin/luci/admin is a redirect to the login form. owlab test logs in first; a hand-written curl does not.
HTTP 200 with a traceback in the bodyLuCI's dispatcher answers 200 when it catches an exception. owlab test fails this; a status-code check does not.
kmod-* installs but does not loadA container shares the host kernel. Kernel modules need fidelity: vm.
Installs start failing a day after they workedYou pinned snapshot. Snapshot images and snapshot feeds are rebuilt daily and independently. Pin exact point releases.
Editing files under images/ changes nothingThe build context is embedded in the binary. Run go build again.
DNS is broken but the network is fineThe container engine does not forward outbound UDP 53. owlab doctor checks this.

Where to look next