hero_db_admin: HTTP 500 "Template 'index.html' not found" — templates loaded from env!("CARGO_MANIFEST_DIR") at runtime #33
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_db#33
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom
A freshly built
hero_db_adminfromdevelopmentHEAD (commit1e00ce3at filing time) returns HTTP 500 on every page route:Direct probe via UDS reproduces the same:
Observed on
mahmoud-ashraf-devbox(65.21.46.99) after deploying a release binary I built on the same host. The Redis-RESP path and JSON-RPC paths still work (redis-cli … PING→PONG); only the web UI is broken.Root cause
crates/hero_db_admin/src/web.rs:125-160loads templates from disk at runtime, usingenv!("CARGO_MANIFEST_DIR")as the base:env!("CARGO_MANIFEST_DIR")is evaluated at compile time, so the build host's path to thehero_db_adminsource crate is baked into the binary. This makes the binary non-relocatable and non-portable across UIDs — even on the same host:The
if let Ok(...)further hides this: whenread_dirreturnsErr, the template vec stays empty, Tera is initialized with zero templates, and every route that callsrender(...)returns 500 with the cryptic "Template 'X' not found" error rather than logging the underlying I/O error.Concrete repro on
mahmoud-ashraf-devboxmahmoud:CARGO_TARGET_DIR=/home/mahmoud/hero/build/target cargo build --release -p hero_db_admin. CARGO_MANIFEST_DIR resolves to/home/mahmoud/work/hotfix/hero_db/crates/hero_db_admin./home/common/hero/bin/hero_db_admin(chown common:common).service.start hero_db_admin.common./home/mahmoudhas modedrwx------(standard adduser default), socommoncannot traverse it.read_dir("/home/mahmoud/work/hotfix/hero_db/crates/hero_db_admin/templates")→Permission denied→ swallowed → 500 on every page.Proof of the permission issue:
The path is baked into the binary (
stringsshows/templatessuffix, prefixed by the manifest-dir constant at runtime).Why the previously-deployed binary worked
The binary that was on the box before this swap (
hero_db_admin.bak-…, 18.9 MB, built May 18) contains noindex.htmlstring and no/templatespath string — it appears to predate the current Tera-from-disk codepath (likely from before thehero_db_ui→hero_db_adminrename, or built with a different asset-loading strategy). So the regression is latent: the moment anyone builds and deploys from currentdevelopment, the UI breaks.Suggested fix
Embed templates at compile time so the binary is self-contained, the same way static assets are now served through
hero_admin_lib::assets::shared_static_handler. Options, in rough order of size of change:rust-embed(smallest diff): deriveRustEmbedon aTemplatesstruct rooted at"templates/", iterateTemplates::iter()increate_web_router, and calltera.add_raw_template(name, str_from_bytes). This is whathero_admin_libalready uses for shared static assets.include_str!per template: simpler if the template set is small/stable. Lose hot-reload-from-disk, gain portability.HERO_DB_ADMIN_TEMPLATESenv var, default<install_prefix>/share/hero_db_admin/templates), and have the release flow install the templates alongside the binary. Largest change but most flexible.While fixing, please also surface the
read_direrror explicitly — silently swallowing it viaif let Ok(...)is what made this turn into a 500-with-cryptic-message rather than a startup failure that would have caught the bug immediately.Workaround in place
Rolled
hero_db_adminonmahmoud-ashraf-devboxback to the pre-existing binary so the UI keeps working. The newhero_db_serverbinary (this is the actualBrokenPipelog-spam fix in #32) is fully deployed and verified — that PR is unaffected by this issue.Impact
hero_db_adminfrom currentdevelopmenton a multi-user box, or shipping the binary from CI to a different host, gets a fully broken admin UI.hero_db_admin) can be merged but cannot be deployed on the devbox until this lands.Research outcomes (before coding)
Surveyed sibling admin crates across the org and the framework helpers to settle on a fix consistent with the rest of the ecosystem rather than a one-off for hero_db. Findings:
Canonical pattern in this codebase
All other admin crates compile templates into the binary at build time, so the binary is relocatable by construction:
hero_proc_adminrust-embedhero_slides_adminrust-embedhero_books_adminmy_compute_zos_adminmy_compute_explorer_adminhero_db_adminenv!("CARGO_MANIFEST_DIR")← this bughero_aibroker_uienv!("CARGO_MANIFEST_DIR")← same latent bugGrep for
env!("CARGO_MANIFEST_DIR")in admin/UI crates returned exactly two production hits: this crate andhero_aibroker_ui/src/main.rs:65-66(Tera::new(format!("{}/templates/**/*.html", env!("CARGO_MANIFEST_DIR"))).expect(...)).hero_aibroker_uiactually.expect()s the result, so on a misaligned deploy it would panic at startup rather than fail with a runtime 500 — better diagnostic, same root cause.Hidden gem: the framework already supports the Tera path
hero_website_lib/src/templates.rs:81-111exposes:Pass any
#[derive(Embed)] #[folder = "templates/"]struct, get back a fully-loaded Tera. It even merges library-default templates on top. So a Tera-based service can become relocatable with ~4 lines of struct + 1 call.Template-syntax audit
All 11 hero_db_admin templates (~5000 LoC) use only
{% extends %},{% block %},{% include %},{{ var }}. Zero Tera-specific filters (| default,| length,| safe, etc.). The templates would migrate to Askama or stay on Tera with no rewrites.Documentation gap
skills/hero/ui/hero_ui_assets.mdcorrectly documentsrust-embedfor static assets.CARGO_MANIFEST_DIR." That's how the bug slipped in.Recommended fix
Two paths exist; recommend doing the small one first and treating the larger one as separate follow-up:
Path B (recommended for this issue) — Tera +
load_embedded_templates. ~20 LoC, mostly deletions. Replace theUI_CRATE_DIR+read_dir(...)block incrates/hero_db_admin/src/web.rs:124-169with:Add
hero_website_lib = { workspace = true }(and confirmrust-embed = { workspace = true }) tocrates/hero_db_admin/Cargo.toml. Delete theUI_CRATE_DIRconst.Why this and not Path A:
render(...)call site → minimal regression risk.EACCES.Path A (follow-up, optional) — migrate
hero_db_adminto Askama. Justifiable on ecosystem-consistency grounds (5-of-6 admin crates use Askama), but a separate, judgment-call refactor with its own diff and review surface. Defer.Additional work this issue should spawn
hero_aibroker_ui— same bug, same blast radius. Track separately so it doesn't block the hero_db_admin fix.skills/hero/ui/(extendhero_ui_assets.mdor createhero_ui_templates.md) documenting: "Templates must be embedded into the binary. Use Askama for new services (preferred — typed, compile-time-checked) orhero_website_lib::templates::load_embedded_templates::<MyTemplates>()for existing Tera services. Never useenv!("CARGO_MANIFEST_DIR")at runtime — it bakes the build host's path into the binary and breaks relocation, cross-UID deploys, and CI artifacts." Link fromhero_ui_dashboard_admin.md.if let Ok(read_dir(...))pattern hid the underlying EACCES. Any future template-loading code should propagate errors so failures are loud.Not taking action on this issue right now (returning to other work). Captured here so whoever picks it up has the full context and doesn't need to re-do the survey.