Docs
Troubleshooting
Fix the most common Proa setup, registry, template, component, island, routing, and CI failures.
Start with the fastest checks. They separate environment problems from template and runtime problems:
proa --help
proa check
proa fmt --check --verify src
proa lint --min-severity info src
proa site check
If you are working from the Proa repository instead of an installed CLI, use the Cargo wrapper:
cargo run -p proa-cli --bin proa -- fmt --check --verify src
cargo run -p proa-cli --bin proa -- lint --min-severity info src
proa: command not found
Right after an install, the cause is PATH. The installer appends an export PATH
line to every shell profile it finds (~/.bashrc, ~/.bash_profile, ~/.zshrc,
~/.profile, and ~/.config/fish/config.fish), and the shell you are already in
never rereads those files. Open a new terminal, or add the directory to the
current shell by hand:
export PATH="$HOME/.proa/bin:$PATH"
Confirm that the binaries landed where you expect:
ls "$HOME/.proa/bin"
"$HOME/.proa/bin/proa" --version
On Windows, the installer writes the directory into your user PATH and into the
terminal it ran in, so that terminal can run proa right away. Every other
process keeps the PATH it started with, so restart any other terminal or editor
before running proa --version there. Check the persisted value with:
[Environment]::GetEnvironmentVariable("Path", "User")
The installer refuses to rewrite a user PATH that contains %VAR% references,
because rewriting one would freeze today's expansion. It warns and prints the
line to add when it finds one.
If you ran cargo install or the installer fell back to a source build, the
binaries live in ~/.cargo/bin instead. Point the same export line at that
directory.
If you passed PROA_NO_MODIFY_PATH or --no-modify-path, the installer left
your profiles, and the Windows user PATH, alone by design, and printed the line
to add. Add it yourself, or run the installer again without that flag.
Every run writes a log that lists which profile files the installer examined,
which it changed, and which already had the line. The installer prints the path
as it exits, and it looks like
/tmp/proa-install-20260813T221500Z-1234.log (under %TEMP% on Windows). Read
that when the PATH change did not land where you expected, and attach it if you
report the problem.
When you are developing inside this repository, run the CLI through Cargo instead:
cargo run -p proa-cli --bin proa -- --help
Then use that same prefix for later commands. See Install Proa and the CLI reference.
Windows Builds And Executable Launches
Use a current stable Rust toolchain with the MSVC build tools installed, then
run proa --version and cargo proa --version. Both entry points should
report the installed CLI version. proa dev, proa check, proa build, and
proa test coordinate the Cargo passes required for interactive components.
Plain cargo check supports Rust type checking and editor feedback; it does
not validate the complete linked browser graph. Plain cargo build and
cargo run cannot produce a linked application containing RSJS islands.
The release workflow currently publishes a Linux x86_64 CLI archive. Windows installation falls back to building from source, so installation includes a Rust dependency build. The first application build also compiles dependencies for the descriptor and final passes. Later runs reuse those Cargo artifacts. Keep the same toolchain, target, profile, and features when comparing cold and warm build times. In PowerShell, enable stage timing before reproducing a slow build:
$env:PROA_BUILD_TIMINGS = '1'
proa build
Remove-Item Env:PROA_BUILD_TIMINGS
For LNK1104 or errors involving a very long artifact path, use a short Cargo
target directory for the same build and dev commands:
$env:CARGO_TARGET_DIR = 'C:\pt'
proa dev
Proa uses shorter directory labels on Windows while retaining complete cache identities. Native linkers and dependency build tools can still impose path limits, so a short target root remains useful for deeply nested checkouts. The shorter layout requires one cold build when upgrading from the old layout; Proa leaves the old artifacts in place. Use a separate target root for unrelated projects if their Cargo builds run concurrently.
For missing tailwindcss.exe, follow the Windows instructions under
Tailwind prerequisite.
proa start runs an exported executable. To choose its location explicitly:
proa build --artifact-out target/release/server.exe
proa start --bin target/release/server.exe
If launching or replacing a compiled file fails, preserve the executable path and numeric OS error from the original diagnostic. A successful retry alone does not identify which Windows feature caused the failure:
| OS error | Meaning and next check |
|---|---|
5 | Access denied. Check file permissions and the security event for that exact path; this code alone does not identify antivirus. |
32, 33 | Another process holds a file or region lock. Stop an older server still using that output, or wait for the process holding the lock to release it. |
577 | Windows could not verify the file's digital signature. Inspect the Code Integrity events for the named executable or DLL. |
These meanings come from Microsoft's system error codes 0–499 and 500–999. Smart App Control can also block untrusted unsigned code. Include the Windows version, active security feature, error code, CLI version, and failing command in a report so the failure can be distinguished from a compiler error or file-lock race.
RSJS Unsupported Shapes At Link Time
unsupported_shapes means the linked backend cannot represent a client
expression or dependency in the selected component graph. It can appear after
Rust compilation because the graph is only complete at link time. The error
includes a Rust-resolved component name when one is available.
Run proa lint <component-source.rs> while editing, then proa check for the
whole project. Source lint does not prove every cross-component constraint.
Use explicitly typed state when inference is ambiguous, a named event handler,
and a small reproduction before changing several parts of a mutation at once.
For example, save.run(draft.get()) passes the current value; browser handlers
cannot implicitly capture arbitrary server-only locals. The exact error and
CLI version are needed to distinguish a compiler limitation from an inference
or server/browser boundary error.
The Installer Cannot Reach The Download Host
The installer fetches release archives from https://proa.so/downloads/cli,
the value of PROA_DOWNLOAD_BASE. A connection failure or a 503 from that
host stops the download before there is anything to verify.
A 503 means the release host behind the bridge is unavailable or rate
limited. The response carries Retry-After: 30, so wait half a minute and run
the installer again. Check the endpoint yourself:
curl --proto '=https' --tlsv1.2 -sS -i https://proa.so/downloads/cli/latest.json
A 200 with a version field means the host is healthy and the problem is
between you and it, usually a proxy or a DNS resolver. If the failure persists,
build the CLI from source instead:
curl -fsSL https://proa.so/install.sh | PROA_BUILD_FROM_SOURCE=1 sh
Or point the installer at another host that serves the same file names:
curl -fsSL https://proa.so/install.sh | PROA_DOWNLOAD_BASE=https://mirror.example/downloads/cli sh
The installer log records the request and what came back. Its path is printed as the installer exits.
No Release Is Published Yet
https://proa.so/downloads/cli/latest.json answers 404 when no proa-cli
release exists. The installer reads that as "there is no version to install"
rather than an archive-download failure, and attempts a Cargo build from
crates.io instead. That requires a published proa-cli crate. An unversioned
source build installs the latest published crate; PROA_VERSION selects an
exact published version.
The source build puts proa and cargo-proa in ~/.cargo/bin, not
~/.proa/bin, so look there when the shell cannot find the binary afterwards.
Pinning PROA_VERSION does not change this. A version with no published
archives answers 404 on the asset URL too.
Checksum Verification Failed
The installer compares the downloaded archive against the SHA256SUMS file for
that release. A mismatch stops the install before the installer unpacks
anything, and it deletes the temporary directory it downloaded into, so nothing
was installed and no unpacked binary was left behind.
A truncated download, a proxy that rewrote the response, or a stale cache all produce this. Run the installer again first. If the mismatch survives a retry, fetch both files and check them yourself:
VERSION="<published-version>"
TARGET=x86_64-unknown-linux-gnu
BASE="https://proa.so/downloads/cli/$VERSION"
curl --proto '=https' --tlsv1.2 -fLO "$BASE/proa-cli-$VERSION-$TARGET.tar.gz"
curl --proto '=https' --tlsv1.2 -fLO "$BASE/proa-cli-$VERSION-SHA256SUMS"
shasum -a 256 --ignore-missing -c "proa-cli-$VERSION-SHA256SUMS"
Use sha256sum --ignore-missing -c on Linux. A clean download on a different
network settles the question: if it verifies there, your first download passed
through something that changed it. If it fails there too, do not install the
archive. Open an issue with the version, the target triple, and the digest you
computed, and attach the installer log. It records the URL it fetched, the
expected hash, and the hash it computed. Its path is printed as the installer
exits.
To keep moving in the meantime, build the CLI from source instead:
curl -fsSL https://proa.so/install.sh | PROA_BUILD_FROM_SOURCE=1 sh
No Prebuilt Binary For This Platform
For now, Proa publishes one native release binary:
x86_64-unknown-linux-gnu, built on Ubuntu 24.04. It supports 64-bit Ubuntu
24.04+ and other glibc 2.39+ Linux systems. On anything else, including older
Ubuntu releases, ARM Linux, Alpine and other musl distributions, macOS,
Windows, FreeBSD, and 32-bit hosts, the installer reports that it found no
matching asset and compiles the CLI with Cargo instead. For an official released
version, this fallback builds from crates.io without GitHub credentials:
cargo install proa-cli --locked
Use --version "=<published-version>" to pin a release. Main snapshots require
a prebuilt archive. If the snapshot archive is unavailable for your platform,
select a published version instead.
That build takes several minutes and needs Rust 1.93.0 or newer plus a C toolchain. On Alpine, install the toolchain first:
apk add build-base
Alpine's packaged Rust lags the 1.93.0 minimum on some releases. Run
rustc --version, and install the toolchain with
rustup when the packaged one is older. The installer
does not install Rust for you: it stops and prints the rustup command
instead. And when the only downloader on the system is BusyBox wget,
which cannot enforce HTTPS-only transfers, the installer stops and asks
for curl before downloading anything:
apk add curl
The source build puts proa and cargo-proa in ~/.cargo/bin, not
~/.proa/bin, so check that directory when the shell cannot find the binary
afterwards.
When the fallback surprises you on a platform that does have a published
binary, read the installer log. It records the raw uname output, the libc it
detected and how, the target triple it resolved, and the asset lookup that came
back empty. Attach it if you report the detection as wrong.
Cargo Cannot Download Proa Crates
Generated projects use public crates.io dependencies. Check network access to
crates.io, the Cargo error, and your project's proxy configuration. No private
proa registry or GitHub token is required.
For an older project using Git dependencies, keep all Proa crates on the same
Git URL and revision, and enable net.git-fetch-with-cli when you need your Git
credential helper or proxy settings. Migrate dependency declarations together;
see Upgrading and CI.
The Page Builds But Styles Are Missing
Generated Tailwind sites write public/styles.css. If the page renders unstyled, rebuild the stylesheet with the same command the proa dev / proa build asset step runs:
tailwindcss -i styles/input.css -o public/styles.css
If tailwindcss is not on PATH, install Tailwind CSS CLI v4 with the Proa installer (curl -fsSL https://proa.so/install.sh | sh -s -- --tailwind-only) or with Homebrew (brew install tailwindcss).
Use --no-tailwind when scaffolding if you want to bring your own stylesheet pipeline.
html_sync! Or html! Does Not Parse
Run the formatter with structural verification first:
proa fmt --check --verify src
Common causes:
| Symptom | Fix |
|---|---|
| A void element has children | Use a self-closing element such as <input ... />. |
| Attribute syntax is split by invalid tokens | Keep names like data-state and aria-label as one attribute token. |
| Dynamic text appears without braces | Use {value} for Rust expressions and quoted text for static strings. |
| A generated macro body has odd spacing | Run proa fmt src, then cargo fmt --all. |
See The html! Macro and Format, Lint, And Check.
A Component Has Trait Errors
Most page and component render paths should implement WebRenderSync. Keep the
implementation generic over the data loader; the output defaults to the owned
RenderBuffer:
use proa::{DataLoader, WebContext, WebRenderSync, WriteError};
impl<L: DataLoader> WebRenderSync<L> for MyComponent {
fn render(self, cx: &mut WebContext<L>) -> Result<(), WriteError> {
// ...
Ok(())
}
}
If you place a sync component inside an async html! tree, wrap it with proa::to_async(...). If the component awaits data or uses async loaders, implement WebRender and render with html!. See Render traits.
Classes Disappear Or ClassList Fails
ClassList<N> has fixed capacity. Count every segment you add, including optional classes:
let classes = ClassList::<4>::new()
.add(BASE)
.add(size.classes())
.add(variant.classes())
.add_opt(self.class);
Prefer increasing the capacity over building a String in the render path. See Class composition.
An Island Renders But Clicks Do Nothing
Check the browser network panel first. The page should load the rsjs runtime and the island chunk without 404s.
Then confirm:
- The island has a matching server-rendered placeholder.
- The route serves static assets from the same origin during development.
- Event handlers live inside the
#[island]or#[rsjs(component, client)]boundary. - Async query helpers use an async
WebRendercomponent, not a sync render implementation.
See RSJS.
A New Route Returns 404
Run the site checks:
proa site routes
proa site check
Confirm that the route is registered in the router, the page module is exported, and generated metadata points at the same path. See Framework routing and Site commands.
CI Fails But Local Checks Pass
Make CI run the same commands as local development:
cargo fmt --all --check
proa check --locked
proa test --locked
If CI fails before compilation, check access to crates.io and the version requirements in Cargo.toml. Generated sites use public registry dependencies and need no GitHub credential or Cargo registry token. Commit Cargo.lock and use --locked to keep CI on the same resolved dependencies as local development.
For GitHub annotations, run:
proa lint --github --changed --base origin/main
Git selection replaces the path argument: --changed and --base cannot be combined with explicit paths.
See CI / GitHub Actions.
Still Stuck
Capture the exact failing command and a machine-readable diagnostic:
proa lint --json --min-severity info src
proa chunks --json src
Include the failing route, the rendered HTML snippet if a browser page looks wrong, and the first compiler or CLI diagnostic that points at your code.
For an install that failed, attach the installer log instead. The installer
prints its path on the way out, on success and on failure, and
PROA_LOG=/path/to/file writes it where you choose. Rerun with PROA_VERBOSE=1
to see the same detail in the terminal while you work.