# Troubleshooting Guide
Root-cause analysis and remediation for common ndaal CSAF-CRUD
failures. Each entry lists the symptom, the cause, and the fix. For
configuration keys see [REFERENCE.md](../REFERENCE.md); for
operations see the [Administrator Guide](Administrator_Guide.md).
## First step — read the logs
Most failures are explained by the logs. Raise the level and watch
stderr or the rolling file:
```bash
RUST_LOG=debug csaf-crud config/default.toml
# file: <log_directory>/csaf-crud.log.<YYYY-MM-DD> (default ./data_log)
```
## Startup
### A port is refused (exit status 64)
- **Symptom:** the server exits at once, before it opens a database,
with a line that names the key and ends with the rule, for example
`csaf-crud: listen_port: port 80 is in the refused range 0-1024
(privileged ports 0-1023 plus 1024, by policy); accepted: 1025-65535`.
`echo $?` prints `64`.
- **Cause:** `listen_port` or `quic_port` in the config file breaks the
rule: a listen port is accepted iff it is a canonical decimal number in
1025..=65535. Status 64 means exactly this and nothing else; a config
file that is not TOML, or a port written as text where a number
belongs, exits with `1` instead.
- **Remedy:** correct the value. What you wrote and why it is refused:
| `80`, `443`, `1023`, `1024` | in the refused range 0-1024 (privileged ports, plus 1024 by policy) |
| `0` | not a fixed port: the operating system would pick any free port |
| `65536` | a number above 65535 is not a TCP or UDP port |
| `-1` | a sign |
| `"08080"` (`quic_port`) | a leading zero (some tools read it as octal) |
| `"+8080"`, `" 8080"` | a sign, a space |
| `"0x1f90"`, `"abc"` | not a decimal number |
A config that used `listen_port = 80` or `443` worked in earlier
releases if the process had the privilege; it stops now. Use a port above
1024 and put a reverse proxy or a firewall redirect in front if clients
must reach 80 or 443. `quic_port` also accepts `"off"` (any case) or `0`
for no HTTP/3 listener, and its message then ends `accepted: 1025-65535,
or off`.
### Port already in use
- **Symptom:** the server exits immediately at startup; the OS
reports an address-already-in-use error. The status is `1`, not `64`:
the port is allowed, something else holds it.
- **Cause:** another process holds the TCP port (default `8180` with
the bundled config, or the compiled fallback `8443`). With QUIC
enabled the UDP port (`quic_port`, by default `listen_port + 1`)
must also be free.
- **Remedy:** find the holder and stop it, or change the port.
```bash
lsof -i :8180 # or :8443
# then edit listen_port in the config TOML and restart
```
### HTTP/3 is off and the log says why
- **Symptom:** the startup log has `HTTP/3 is off: quic_port is off, or
listen_port is 65535 and there is no UDP port above it`, nothing
listens on a UDP port, and the System Information page says `off` in
the `HTTP/3 Address (UDP)` row. HTTP/1.1 and HTTP/2 work.
- **Cause:** either `quic_port` is `"off"` or `0`, or `listen_port` is
65535 and `quic_port` is not set: the default HTTP/3 port is
`listen_port + 1`, and 65536 is not a port.
- **Remedy:** none needed if that is what you configured. To serve
HTTP/3 at `listen_port = 65535`, set `quic_port` to a UDP port of your
choice (1025-65535). TCP and UDP are separate port spaces, so
`quic_port = 65535` is valid too.
### The server stops at start with "attempt to add with overflow"
- **Symptom:** the log shows `Listening (TLS 1.3 ...)` on port 65535, then
`panicked ... attempt to add with overflow` and the status is `101`.
- **Cause:** releases before the port rule computed the HTTP/3 port as
`listen_port + 1` in a 16-bit number, which does not fit at 65535.
- **Remedy:** upgrade to a release with the port rule, which starts at
65535 with HTTP/3 off. Until then, use a `listen_port` of 65534 or
lower.
### Data directory not writable
- **Symptom:** startup fails just after the banner with a storage or
database open error.
- **Cause:** `data_dir` (or its parent) is not writable by the
service user. Directory creation is best-effort and is silently
skipped, so the subsequent database open is what fails.
- **Remedy:** create the directory and grant ownership:
```bash
mkdir -p /var/lib/csaf-crud/data
chown csaf:csaf /var/lib/csaf-crud/data
```
### TLS certificate generation fails
- **Symptom:** startup error mentioning the private key or
certificate parameters.
- **Cause:** the self-signed certificate could not be generated
(commonly a badly skewed system clock — the certificate is valid
from now to now + 45 days), or a configured `tls_cert` / `tls_key`
pair is missing or not valid PEM.
- **Remedy:** correct the system time, or fix/clear the `tls_cert`
and `tls_key` paths so the server falls back to auto-generation.
### redb database locked or corrupt
- **Symptom:** startup fails with a storage error.
- **Cause:** redb allows only one process to open a database file. A
second instance, or a corrupt `csaf.redb`, fails to open.
- **Remedy:** ensure no other instance is running
(`lsof | grep csaf.redb`). If the file is corrupt, restore it from
a dump (see the Administrator Guide); a fresh file is created if it
is removed.
### SQLite open or migration fails
- **Symptom:** startup error `SQLite open failed: …`.
- **Cause:** a corrupt `csaf.sqlite`, stale write-ahead-log
companions, or a failed schema migration.
- **Remedy:** with the server stopped, restore from a dump, or remove
the stale companions if recent writes can be lost:
```bash
rm data/csaf.sqlite-wal data/csaf.sqlite-shm
```
### QUIC / HTTP3 listener fails to start
- **Symptom:** an error log `QUIC server failed`, but the service
keeps serving over TCP.
- **Cause:** the UDP port (`quic_port`, by default `listen_port + 1`)
is unavailable. This is non-fatal by design — the QUIC listener is
spawned independently.
- **Remedy:** free the UDP port if HTTP/3 is needed; otherwise the
TCP listener (HTTP/1.1 + HTTP/2) is unaffected.
### No HTTP/3 on Windows ARM64
- **Symptom:** the Windows ARM64 server answers over HTTP/1.1 and
HTTP/2 only. The startup log has no `QUIC/HTTP3 listener spawned`
line, nothing listens on UDP port `listen_port + 1`, and there is
no `QUIC server failed` error. If that error does appear, see the
previous entry instead.
- **Cause:** the `aarch64-pc-windows-msvc` build of `csaf-crud` in
releases up to and including 1.5.23 was built without the `quic`
feature, so it contains no HTTP/3 listener. This is not a port
conflict and no setting enables it. All other targets include
HTTP/3, and release 1.5.24 and later include it on Windows ARM64
too.
- **Remedy:** upgrade to release 1.5.24 or later. Until then, use
HTTP/2 over TCP, which this build serves over TLS 1.3 on the
configured `listen_port`. After the upgrade, the startup log shows
`QUIC/HTTP3 listener spawned`.
### Process killed after HTTP/3 traffic (releases before 1.5.26)
- **Symptom:** the memory or CPU use of the `csaf-crud` process climbs
after a client connects to the UDP port (`listen_port + 1`), and the
process may then be killed. That also ends the TCP listener, so
HTTP/1.1 and HTTP/2 stop too. It needs `listen_addr` set to `::` or a
routable address; the default `127.0.0.1` cannot be reached from
another host.
- **Cause:** `quinn-proto` 0.11.18 or older (release 1.5.25 bundles
0.11.17; GHSA-53rm-773f-4q8c). After the QUIC handshake, one small
frame makes the endpoint open up to 2^40 streams. The server asks for
no client certificate, so any peer that completes the handshake can
trigger it. This was assessed from the upstream advisory and the
source, not reproduced against a running server.
- **Remedy:** upgrade to release 1.5.26 or later; `csaf-crud --version`
shows what is running. Until then, keep `listen_addr` on loopback,
block UDP on `listen_port + 1` at the firewall, or build with
`--no-default-features` to drop HTTP/3. See
[Releases that fix a security flaw](Administrator_Guide.md#releases-that-fix-a-security-flaw).
### High CPU use on the UDP port on macOS (releases before 1.5.26)
- **Symptom:** on macOS, the CPU use of `csaf-crud` stays high after a
truncated UDP datagram reaches the HTTP/3 port.
- **Cause:** `quinn-udp` before 0.5.16 (GHSA-6pp4-4cxf-xf88) can repeat
non-blocking reads on the Unix and Apple fallback receive path before
the packet is authenticated. The Linux and Windows builds use other
receive paths, which upstream states differ from the affected one.
- **Remedy:** upgrade to release 1.5.26 or later, or block UDP on
`listen_port + 1` at the firewall.
## TLS / certificates
### Browser or curl rejects the certificate
- **Symptom:** the browser warns the connection is not private; curl
reports a self-signed-certificate problem.
- **Cause:** the development certificate is self-signed and untrusted.
- **Remedy:** accept the exception in the browser, or pass `-k` /
`--insecure` to curl. For production, configure a trusted
`tls_cert` / `tls_key`.
### Certificate suddenly expired
- **Symptom:** clients reject the certificate after about six weeks.
- **Cause:** the auto-generated certificate is valid for 45 days.
- **Remedy:** restart the server to regenerate it, or install a
managed certificate via `tls_cert` / `tls_key` and rotate it before
expiry.
## Import
### Import directory does not exist
- **Symptom:** import fails immediately:
`Import directory does not exist: …`.
- **Cause:** the configured `import_directory` is missing.
- **Remedy:** create it or correct the path in settings.
### A file is skipped
- **Symptom:** the file is counted as skipped, with an error line.
- **Causes and remedies:**
- Malformed JSON — fix the syntax (the error names the line and
column).
- Valid JSON but not a CSAF document — a deserialization error
names the missing field; correct the structure.
- Hard validation error — the message lists the failing rules; fix
the document and re-import.
- `provider-metadata.json` is intentionally skipped and is not an
error.
### Imported document later refuses to export
- **Symptom:** import succeeds with an OASIS-conformance warning in
the log, but a later export of the same document is refused.
- **Cause:** import is lenient (warnings admitted); export is strict.
- **Remedy:** fix the OASIS findings on the document, then re-export.
## Export
### Export refused for a document
- **Symptom:** export reports
`OASIS validation failed for '<id>': …` or
`validation failed for '<id>': …`.
- **Cause:** the document fails the OASIS export gate, often because
`csaf_mode` is `2.0` while the document is structurally 2.1
(it carries `metrics` / `cvss_v4`), so relabelling it produces a
non-conformant file.
- **Remedy:** set `csaf_mode` to match the documents (use `2.1` for
2.1-structured advisories), or fix the document, then export again.
### Export directory not writable
- **Symptom:** export fails with a permission or I/O error.
- **Cause:** `export_directory` is not writable.
- **Remedy:** grant write access to the service user.
### Tracking ID rejected as unsafe
- **Symptom:**
`tracking ID '<id>' resolves outside the export directory`.
- **Cause:** the tracking ID contains path-traversal characters
(`..` or separators); the export path-safety check blocks it before
any filesystem call.
- **Remedy:** use a conventional tracking ID
(for example `ndaal-sa-2026-003`).
## Validation
### Where errors appear
- **Web:** hard errors redisplay the form with a banner listing each
finding's JSON path and message; your input is preserved.
- **CLI:** findings print as `[ERROR]` / `[WARN]` lines; any hard
error exits non-zero.
- **API:** hard errors return `422` with an `errors` array. A
finding has a `path`, a `severity` (`error` or `warning`), and a
`message`. Warnings alone never block.
## Database (runtime)
### SQLite database is locked
- **Symptom:** a request fails with a database-locked error after a
short delay.
- **Cause:** a long write held the single SQLite writer past the
5-second busy timeout. SQLite runs in WAL mode (concurrent readers,
one writer).
- **Remedy:** retry; avoid concurrent bulk writes against the same
instance. Persistent locking usually means another process has the
file open.
### Foreign-key constraint failed
- **Symptom:** an audit write fails with a foreign-key error.
- **Cause:** foreign keys are enforced; an audit row referenced a
non-existent user.
- **Remedy:** this indicates inconsistent data — restore from a dump
if it recurs and capture the debug logs.
## Web interface
### Only the logo shows in the navigation bar
- **Symptom:** on a window 992 px wide or wider, the navigation bar
shows only the logo and "CSAF". The page links, theme toggle,
language menu, and Info menu are missing, and there is no menu
button.
- **Cause:** releases 1.5.23 and 1.5.24 wrap those controls in a
closed `<details>` element and dissolve it with `display: contents`
on wide screens. Current browsers keep the content of a closed
`<details>` hidden through `::details-content`, and that rule
still applies when the element itself is dissolved. Checked in
Chrome 152 against the published 1.5.23 binary; the navigation
markup and CSS are identical in 1.5.24. Firefox and Safari were
not tested. Below 992 px the menu button works as intended.
- **Remedy:** upgrade to 1.5.25 or later, which no longer wraps
the desktop navigation in a closed `<details>`. On 1.5.23 or 1.5.24,
make the window narrower than 992 px, or zoom the page in (Ctrl/Cmd
and +) until the menu button appears, then use it. Alternatively, open
the pages by address: `/`, `/csaf`, `/admin/import`, `/admin/export`,
`/settings`.
### The page is unstyled, or menus and dialogs do nothing
- **Symptom:** the page shows plain text without the white header and blue
accents, or the menus, the Theme choice and the delete confirmation do
not react. The browser console reports "Refused to apply inline style",
"Refused to execute inline script" or "violates the following Content
Security Policy directive".
- **Cause:** every response carries a strict `Content-Security-Policy`
(see "Security headers" in the Administrator Guide). A reverse proxy,
a browser extension or a security product that injects its own
`<style>`, `<script>` or `Content-Security-Policy` header makes the
browser block the page's own files or the injected code. The server
itself emits no inline code.
- **Remedy:** request the page directly (`curl -sk -D - -o /dev/null
https://127.0.0.1:8180/`) and compare the headers with the table in the
Administrator Guide. If the direct response is right, remove the
injection or the extra header from the proxy. Do not loosen the policy
on the server: the tests fail on `unsafe-inline`.
### The theme choice does not stick
- **Symptom:** you pick Dark in the Theme menu, but the next page or the
next visit is light again.
- **Cause:** the choice is stored in the `csaf_theme` cookie, which is
`Secure` and `SameSite=Lax`. A browser that blocks cookies for the
site, a private window that is closed, or a plain-HTTP address (the
cookie is never sent) all lose it. Without a cookie the page uses the
default under Settings.
- **Remedy:** open the site over HTTPS and allow cookies for it. To make
dark the default for everyone who has not chosen, set Theme to Dark
under Settings.
### The browser now insists on HTTPS for other services on the same host
- **Symptom:** after opening csaf-crud with a trusted certificate, other
services on the same host name (other ports, subdomains) are only
reachable over HTTPS, with no "continue anyway" option.
- **Cause:** `Strict-Transport-Security: max-age=31536000;
includeSubDomains` tells the browser to use HTTPS for the whole host
name and its subdomains for a year. It is ignored on the default
self-signed certificate, so this appears only once a certificate your
browser trusts is configured.
- **Remedy:** serve those services over HTTPS, or remove the host from
the browser's HSTS store (in Chromium browsers: `chrome://net-internals/
#hsts`, "Delete domain security policies"). To avoid it on a shared
host name, run csaf-crud on its own name.
### A page shows a lone comma or a text such as `csaf.form_section_publisher`
- **Symptom:** the Import, Export, Dump or Audit-export page shows a single
`,` where an explanation belongs, the audit-export result lacks its row
counts, or a heading or table row reads `csaf.form_section_publisher`,
`csaf.list_pagination_previous` or `csaf.form_edit_heading_prefix`.
- **Cause:** releases up to and including 1.5.24 shipped seven admin texts
that were a single comma in all 49 languages, and four translation keys
that the code used but no table defined, so the key itself was shown.
- **Remedy:** upgrade to 1.5.25 or later. A guard test now fails the
build if any translation has no letter or digit, or if a route module
uses a key that `en.tsv` lacks.
### The language or theme redirect goes to the home page
- **Symptom:** choosing a language or a theme without JavaScript returns
you to `/` instead of the page you were on.
- **Cause:** both routes return to the path of the `Referer` header. A
proxy, a privacy setting or `Referrer-Policy: no-referrer` on a
surrounding page removes the header, and the routes then fall back to
`/` by design (never to a host taken from the request).
- **Remedy:** none needed; the choice is still stored. Allow the
`Referer` for same-origin requests if you want to return to the same
page.
## Building from source
### cargo hangs on macOS, or a newly built program never starts
- **Symptom:** `cargo build`, `cargo test` or `cargo clippy` stops
responding. `ps` shows `build-script-build` processes in state `S` at
0% CPU, for minutes to hours. To confirm, compile and run a trivial
program; if it prints nothing within 20 seconds, this is the problem:
```bash
printf 'fn main(){println!("hello");}\n' > hello.rs
rustc hello.rs && ./hello
```
- **Cause:** seen on 2026-10-19, when `XprotectService` used about 375%
CPU and `syspolicyd` about 65% while every freshly built executable
hung. The macOS malware scan of new executables is the likely cause.
That is inferred from the process list and the trivial program, not
proven. On 2026-10-20 the same check passed on the same host, so it
comes and goes.
- **Remedy:** stop your own hung `cargo` and `build-script-build`
processes. Do not stop `XprotectService` or `syspolicyd`; they are
macOS security services. A reboot is one way out. The other is to run
the build and the gates in a Linux container; see
[Newly built programs hang on macOS](Developer_Guide.md#newly-built-programs-hang-on-macos).
Commands that do not run a new executable still work, among them
`cargo update`, `cargo tree`, `cargo audit` and `cargo deny check`.
## Health check
Confirm the service is up and both stores are open:
```bash
curl -k https://127.0.0.1:8180/api/v1/system/health
```
A `200` with `status: healthy` means redb and SQLite are both
available; `503` (`degraded`) means a store is down — check the logs
for the startup errors above.