FAQ
Grouped by scenario: startup, modules, permissions, routing, API IDs, and deployment.
Startup & install
python run.py says "no such table"
Tables are not auto-created at startup. First-time setup:
make initdb # equivalent to tortoise init-db + first-time seeds
# or
make mm # makemigrations + migrateFor ongoing model changes, use make mm. See Commands.
Redis connection fails
redis-cli ping # should return PONGOr temporarily point to a local instance:
REDIS_URL=redis://localhost:6379/0Port already in use
Backend: 9999, frontend: 9527, Nginx: 1880. Adjust in run.py or web/vite.config.ts.
Module development
My manually-created menus / buttons disappeared after restart
Your init_data.py calls reconcile_menu_subtree(...) — that subtree is now in IaC mode and only menus / buttons declared in init_data.py survive. Web-UI-created entries are reaped on every restart.
If a subtree needs to allow user-created menus, don't call reconcile_menu_subtree on it.
Startup logs show ensure_role 'XXX': missing apis [...]
The declared (method, path) no longer exists in the Api table. Common causes:
- A route was renamed / removed but the role seed
apislist wasn't updated - Typo (note method is lowercase:
"post"not"POST")
Fix on sight — otherwise the role's permission silently drops. See Init data / drift warnings.
I removed a role from init_data but it's still in the DB
ensure_role is upsert-only — it does not delete what's gone. Removal goes through a migration:
# migrations/app_system/
async def upgrade(db):
await db.execute_query("DELETE FROM roles WHERE role_code = 'R_OLD'")Same applies to business seed data. This is intentional — auto-deletion has surprising cascades.
My new business module's routes aren't mounted
Check startup logs:
Business: module 'inventory' discovered but has no api.py or api/ package — routes will not be registeredAdd api/__init__.py that exports router: APIRouter.
Business: module 'inventory' api module does not export a valid 'router' (APIRouter) objectMake sure api/__init__.py actually has router = APIRouter() (or assigns it from a sub-router).
See Autodiscover / common drift.
Temporarily disable a business module
mv app/business/inventory app/business/_inventoryautodiscover skips directories starting with _.
CLI-generated code has // TODO
Foreign keys / custom enums can't be auto-resolved into dropdown sources. Search the codebase for TODO and fill in the options data source (typically a fetchGetXxxList API call).
Permissions
I changed a role / menu but the user still has stale permissions
Permissions live in Redis cache. After CUD, refresh:
from app.core.cache import load_role_permissions, load_user_roles
await load_role_permissions(redis, role_code="R_HR_ADMIN")
await load_user_roles(redis, user_id=123)Or just restart — refresh_all_cache runs at startup.
I revoked a user but their old token still works
After password change / forced logout:
from app.system.services.auth import invalidate_user_session
await invalidate_user_session(redis, user_id)This does INCR token_version:{uid}. Old tokens fail with 2106 SESSION_INVALIDATED on next request. See Auth / token_version.
Permission denied (2201) but the role is granted the menu
Menu and API are two separate dimensions. Role seeds must grant both:
await ensure_role(
role_code="R_HR_ADMIN",
menus=[..., "hr_employee"],
apis=[
("post", "/api/v1/business/hr/employees/search"),
("post", "/api/v1/business/hr/employees"),
],
)A department manager can see the entire company's data
The seed didn't declare data_scope explicitly. The model default is all = full access.
{
"role_code": "R_DEPT_MGR",
"data_scope": DataScopeType.department, # ← required
}See Data scope.
Routing (frontend)
Editing .env doesn't take effect
Restart the Vite dev server.
Route doesn't show in the menu
Check hide_in_menu=True on the menu, or that the current role doesn't have the route_name in its menus.
Static vs dynamic routing
VITE_AUTH_ROUTE_MODE=static— frontend defines routes, filtered byrolesmetaVITE_AUTH_ROUTE_MODE=dynamic(default) — backend issues per-user routes viaGET /api/v1/route/user-routes
CORS error
Dev: Vite proxy (vite.config.ts server.proxy). Prod: Nginx reverse proxy (see Deployment).
Production 404
Make sure Nginx has try_files $uri $uri/ /index.html; — otherwise refreshing nested routes 404s.
Vue "multi-root template" error
Page transitions use <Transition> which requires a single root:
<template>
<div>...</div> <!-- ✅ -->
</template>API IDs
Frontend gets ID like Yc7vN3kE, not a number
That's a sqid — public-facing IDs are sqids. Frontend doesn't decode; just send it back as-is and the backend (SqidPath / SqidId) decodes automatically.
Tests passed by sending numeric IDs?
SqidId._sqid_to_int accepts int, numeric string, and sqid during the migration period. Tighten in source after migration completes.
After deploy, all external sqid links broke
SECRET_KEY was rotated — sqid alphabet derives from SECRET_KEY. See Sqids / rotating SECRET_KEY.
Deployment
Every request gets banned by guard inside the container
Deployed behind Nginx without proxy-headers — all requests look like they're from the Nginx container's IP. Fix:
PROXY_HEADERS_ENABLED=true
TRUSTED_HOSTS=["10.0.0.0/8"]How do I unblock an IP from guard
redis-cli --scan --pattern "fastapi_guard:*" | xargs redis-cli delOr temporarily set GUARD_ENABLED=false and restart.
Switched DB and got "module not found: asyncpg"
The driver isn't installed:
uv add asyncpg # postgres
uv add asyncmy # mysql
uv add asyncodbc # mssqlMulti-worker startup duplicates seeds?
No — _run_init_data uses Redis lock app:init_lock to elect a leader. Only the leader runs init.
redis-cli get app:init_done # should be "1"Docker
View backend logs
make logs # all
make logs SVC=app # backend only; SVC=nginx|redis, TAIL=N for line count
docker compose logs -f app # equivalent native formUpdate deployment
git pull
make down && make upWrong timezone in container
Tortoise defaults to use_tz=False, timezone=Asia/Shanghai. For UTC: change TORTOISE_ORM["use_tz"]=True and timezone="UTC" in app/core/config.py.