Operations and Troubleshooting
Deployment checklist, failure diagnosis, and validation guidance for the Enezaframework dashboard system. Part of the Dashboards series.
Table of Contents
- Deployment Checklist
- Diagnostic Order
- Troubleshooting Matrix
- Symptom: The Report Is Missing from the Data-Source List
- Symptom: unknown_report or "not a registered dashboard data source"
- Symptom: Field, Measure, or Aggregation Not Available
- Symptom: Chart Requires Series, Line, or Size
- Symptom: Table or Preview Columns Rejected
- Symptom: Layout Validation Errors
- Symptom: Unknown Parameter or Filter Field
- Symptom: Widget Is Empty
- Symptom: Published Dashboard Is Not Visible
- Symptom: Seeding Skips or Overwrites Work
- Symptom: Designer Access Denied
- Symptom: Comparison Shows a Dash
- Error Codes
- User Validation Checklist
- Related Guides
Deployment Checklist
Run these steps in order when deploying dashboard or report changes.
- Install and migrate the apps:
enezadashboard,enezareporting, and the business modules that define dashboards. - Import report modules at startup so report classes register.
- Validate definitions:
bash python manage.py seed_dashboards --dry-run --json - Seed definitions:
bash python manage.py seed_dashboards - Sync RBAC:
bash python manage.py rbac_sync - Grant permissions for report outputs and the dashboard manage action.
- Restart the application process if report code changed, so the process-local discovery cache is rebuilt.
- Verify
/dashboards/<module>/for a real user, including a user without the designer action. - Check that drafts did not accidentally replace published versions.
Do not skip the dry run. It catches missing reports, invalid layouts, unknown widget types, and bad parameter names before the database changes.
Diagnostic Order
When a dashboard is broken, work from the data outward:
flowchart LR
A[Is the report registered?] --> B[Is it eligible?]
B --> C[Does the user have output permission?]
C --> D[Does the widget config match metadata?]
D --> E[Does the draft pass publication?]
E --> F[Is the dashboard published and visible?]Most failures are at the first three steps, not in the renderer.
Troubleshooting Matrix
| Symptom | Most likely cause | Fix |
|---|---|---|
| Report absent from the data-source list | Not opted in, not registered, ineligible, or wrong app label. | See the dedicated section. |
| Widget says report is unknown | report_ref is stale or the code changed. |
Update the reference, clear the discovery cache, re-publish. |
| Widget says forbidden | Missing output.*.view permission or report test_func. |
Run rbac_sync, grant permission, re-check the report's access function. |
| “Measure is not available” | Field is not a report column/alias, or the widget uses the wrong name. | Use canonical metadata names. |
| “Chart value field must be a numeric measure” | The field is text or not a measure. | Choose a numeric/computation measure. |
| “This chart type requires a series field” | Stacked/heatmap chart missing series_field. |
Add a dimension. |
| “A bubble chart requires a size field” | Missing or non-measure size_field. |
Add a numeric measure. |
| “Table column … is not available” | Column not in the report. | Add it to columns or remove it from the widget. |
| “Widget width must be between 2 and 12” | Invalid layout. | Correct x, y, w, h. |
| “Unknown report parameter” | Parameter not in the report's parameter_spec. |
Use an allowed name or remove it. |
| “Filter field … is not available” | Filter field not exposed by the report. | Enable use_query_builder and expose the field; or remove the filter. |
| Widget is empty | Period, entity scope, filters, or no data. | See Widget is empty. |
| Published dashboard returns 404 | Not active/published, no published version, access code, or module access. | See Published dashboard is not visible. |
| Designer returns 403 | Missing action.enezadashboard.manage. |
Run rbac_sync and grant the action. |
| Seed skips a dashboard | It is admin-owned. |
Use --force or take ownership intentionally. |
| Seed overwrote browser edits | The dashboard is seed-owned. |
Take ownership before editing in the browser. |
| Seed removed tabs/widgets | Prune is on by default. | Use --no-prune or include them in the definition. |
| Comparison shows “—” | No comparison period, report has no date, or previous value is null/zero. | See Comparison. |
| View report link is missing | report_url_name is empty or cannot reverse. |
Register the URL or correct the name. |
| New report code not visible | Process-local metadata cache. | Restart or call clear_dashboard_report_cache(). |
Symptom: The Report Is Missing from the Data-Source List
Work through these checks in order.
1. Is dashboard_enabled set?
class SalesReport(TabularReportView):
dashboard_enabled = True
Without it, no dashboard surface shows the report.
2. Is the class imported at startup?
Registration happens when Python defines the class. If the module is never imported, it never registers. Check:
myapp/views/__init__.pyimports the report module;myapp/urls.pyimports the class;- the app is in
INSTALLED_APPS.
3. Is the report type supported?
Only TabularReportView, SummaryReportView, and DetailedReportView are supported.
Crosstab, time-series, document, and drill-down reports are excluded in v1.
4. Does the report resolve a model?
report_model must be a model class, or get_report_model() must return one.
A report with no model is ineligible.
5. Do the columns parse?
A malformed column list makes the report ineligible. Test it through the metadata endpoint or a shell call:
from enezareporting.dashboard import get_dashboard_report_source
source = get_dashboard_report_source("myapp.salesbyitem")
print(source.meta.columns)
If parsing fails, the source is not returned and the error reason is internal. Fix the column definition first.
6. Is the report in the same app as the dashboard module?
The widget editor's report dropdown is filtered by the dashboard's module:
list_dashboard_report_sources(app_label=module or None, ...)
That means a dashboard owned by module myapp only offers reports whose model
app label is myapp when you pick a report in the widget form. The current
value is always kept in the choices for editing, and a seeded definition can
reference a report from another app, but editors will not see that report as a
new option (enezadashboard/forms.py:694-702).
The create form's initial data-source list is not module-filtered, so a report may appear during creation even though it will not be offered later in the widget editor.
If you need cross-app reporting, create the dashboard in the report model's app,
or seed the widget definition with the cross-app report_ref.
7. Does the user have the output permission?
list_dashboard_report_sources(user=..., request=...) filters by
output.<app_label>.<report_slug>.view. Run rbac_sync and grant the
permission.
8. Is the discovery cache stale?
Discovery is cached per process. After changing report code:
from enezareporting.dashboard import clear_dashboard_report_cache
clear_dashboard_report_cache()
or restart the process.
9. Is the code duplicated?
Two reports with the same report_ref raise ImproperlyConfigured during
discovery. This usually appears as a 500 on the designer page. Make one code
unique or set an explicit dashboard_code.
10. Is it a URL problem only?
A report with an empty or unreversible report_url_name is still eligible. Only
the View report link is blank. Do not confuse that with a missing data
source.
Symptom: unknown_report or "not a registered dashboard data source"
This means a widget, definition, or request used a code that discovery does not know.
| Check | Action |
|---|---|
Typo in report_ref |
Compare with discover_dashboard_reports() keys. |
dashboard_code changed |
Update every widget and definition that references the old code. |
| Report renamed | The default slug follows the class name; rename changes the code unless report_slug/dashboard_code pins it. |
| Report not imported | Import it at startup. |
| Report ineligible | Fix opt-in, type, model, or columns. |
| Cache stale | Clear the discovery cache or restart. |
After correcting references, re-run the seeder or update the draft in the designer and publish.
Symptom: Field, Measure, or Aggregation Not Available
Dimensions
A widget dimension must be a parsed report column. The most common cause is a
group_by field that is not in columns:
group_by = "item_code", "item_description"
columns = ["item_code", SumItemQuantity] # item_description missing
Here item_description cannot be a chart category or table column. Add it to
columns.
Measures
A measure must be:
- a computation field, or
- a numeric model field that appears in
columns.
Text, date, boolean, and relation fields are not measures. A chart
value_field must be in meta.measures specifically
(enezadashboard/widgets/chart.py:76-80).
Aggregations
- Numeric model fields:
sum,avg,min,max,count. - Computation fields: only their own method name, usually
sum.
If you ask a Sum computation field for avg, validation reports that the
aggregation is not supported. Create a separate Avg computation field instead.
Names
Use the canonical column name:
sum__item_quantity
The class attribute SumItemQuantity is exposed as an alias and may work, but
canonical names are stable across refactors.
Symptom: Chart Requires Series, Line, or Size
These are structural requirements, not optional polish:
| Chart type | Required extra field |
|---|---|
heatmap, stacked_bar, stacked_area |
series_field (dimension) |
combo |
line_field (measure) |
bubble |
size_field (measure) |
scatter, bubble |
numeric category_field |
gauge |
measure or value_field, plus optional target/scale_max |
The requirement matrix is in Dashboard Reference — Chart.
If the field exists in the report but is not offered, check that it is in
columns.
Symptom: Table or Preview Columns Rejected
- Every configured column must be a report column or alias.
- Leave
columnsempty to use the first six report columns. page_sizemust be between 1 andMAX_PAGE_SIZE(default 100).row_limitmust be between 1 andMAX_PREVIEW_ROWS(default 20).
A preview is not paginated; it is a short table. If you need paging, use a
table widget.
Symptom: Layout Validation Errors
Publication validates:
x >= 0,y >= 0;w2–12;h2–8;x + w <= 12.
The drag endpoint additionally caps x at 11 and y at 200
(enezadashboard/views/designer.py:645-654).
Common fixes:
{"x": 9, "y": 0, "w": 3, "h": 2} # valid KPI at the right edge
{"x": 6, "y": 0, "w": 6, "h": 4} # valid half-width chart
{"x": 8, "y": 0, "w": 6, "h": 4} # invalid: x + w = 14
If a definition omits layout, the seeder applies a type default. Do not rely
on that for a deliberate composition; set the coordinates.
Symptom: Unknown Parameter or Filter Field
Unknown parameter
parameter_config keys must be allowed by meta.parameters:
DateRangeParam→start_date,end_date;- other parameters → their
nameandform_fields.
Remove or rename the key. A parameter that exists in the report's form but is not
declared in parameter_spec is not allowed.
Unknown filter field
filter_config rules must use IDs from meta.filter_fields. These come from
get_report_filters_by_model() and require use_query_builder = True.
If meta.filter_fields is empty:
- set
use_query_builder = Trueon the report; - confirm the report's filter metadata method returns fields;
- re-check the metadata endpoint;
- clear the discovery cache if you just changed the report.
Operators must be supported by the workflow engine. Use equal as the safe
default.
Symptom: Widget Is Empty
Work through the data path:
- Period — the dashboard defaults to the site
DEFAULT_PERIOD(last_30_daysunless overridden). A tab or dashboard may set a narrower range. Try All/a wider custom range. - Reference date field — if the chosen field has nulls or is not the field
the user expects, the period may exclude all rows. Use the report's
date_fieldor a populated date field. - Entity scope —
dashboard_entity_scopedfilters torequest.current_entity. A user with no current entity or a different entity sees fewer rows. - Page filters — remove user filters and admin
filter_configrules to isolate. - Report data — the underlying model may simply have no records in range.
- Permissions — a forbidden report shows an error rather than an empty widget; do not confuse the two.
- Grouping — a report grouped by a field that is empty produces blank categories. Check the underlying data.
If the widget displays “This widget could not be loaded”, the renderer caught an
unexpected exception. Check the enezadashboard server log; the UI deliberately
hides details (enezadashboard/services/renderer.py:874-892).
Symptom: Published Dashboard Is Not Visible
dashboard_is_visible() requires all of:
is_active = True;publication_status = "published";published_versionis not null;- the module exists and the user can access the module menu;
- when
access_codeis set, the user has that permission.
Source: enezadashboard/services/access.py:40-57.
Checklist:
- [ ] The dashboard was published, not just saved as a draft.
- [ ]
published_versionpoints to a version. - [ ]
is_activeis true. - [ ]
access_codeis empty or the user has it. - [ ] The module exists and the user can access its module menu.
- [ ] At least one tab has an active widget.
A module with no visible dashboard shows the No dashboard designed state rather than falling back to a list page. That is expected behavior.
Symptom: Seeding Skips or Overwrites Work
Skipped
The dashboard is managed_by = "admin". Seeding protects administrator work.
Use --force only when you intend to overwrite it:
python manage.py seed_dashboards --dashboard <code> --force
Overwritten
The dashboard is managed_by = "seed". Browser edits are not protected. Take
ownership in the designer before making persistent edits, or move the change
into the definition.
Tabs/widgets disappeared
Prune is on by default. A definition that omits a tab or widget deletes it from
the draft. Use --no-prune for partial definitions.
Default conflict
Only one active default per module is allowed. If another administrator-owned
dashboard is the default, seeding raises default_conflict unless forced.
Resolve ownership or clear the other default deliberately.
Duplicate definition
Two sources define the same (module, code). load_definitions() raises
ValueError. Keep one source of truth.
“No dashboard definitions found”
- The app config does not expose
dashboards. - The settings list is empty.
--moduleor--dashboardfiltered everything out.- The module is not installed.
Data settings did not apply
data_settings (reference_date_field, limit, default_period) is not
part of the seed schema. Set it in the designer dashboard form. If it must be
code-controlled, extend the seeding service rather than adding an ignored key.
Symptom: Designer Access Denied
The designer requires:
- an authenticated session;
- the RBAC action
action.enezadashboard.manage.
Fix:
python manage.py rbac_sync
Then grant the action to the appropriate role. The action is registered at app
startup (enezadashboard/apps.py:9-16) and enforced before every designer view
(enezadashboard/views/designer.py:153-160).
If the page loads but the data-source list is empty, this is a report permission problem, not a designer action problem.
Symptom: Comparison Shows a Dash
KPI comparison and line/area comparison require:
- a resolved current period with a start and end;
- a comparison choice of
previousorprevious_year; - a report with date support (
DateRangeParamor a resolvable reference date); - a non-null, non-zero previous value.
Only KPI and line/area charts support comparison
(enezadashboard/services/renderer.py:604-619).
A dash is normal when the previous value is null or zero; the delta is undefined.
Error Codes
| Code | Surfaces in | Short fix |
|---|---|---|
invalid_config |
Widget validation, publication | Correct the widget config. |
unknown_report |
Widget render, definition validation | Fix report_ref; clear the cache. |
report_forbidden |
Widget render | Grant the output permission; run rbac_sync. |
report_not_eligible |
Report execution | Fix opt-in, type, model, or columns. |
invalid_parameters |
Report execution | Use allowed parameter names and values. |
invalid_filter |
Report execution | Use allowed filter fields/operators. |
filters_not_supported |
Filter validation | Set use_query_builder = True or remove the filter. |
filters_unavailable |
Filter validation | Install/configure the workflow filter engine. |
invalid_date_field |
Period override | Choose a real date field. |
unsupported_aggregate |
KPI aggregate | Use a supported computation/numeric field. |
invalid_data |
Chart render | Remove negative values or change chart type. |
query_failed |
Widget render | Inspect the server log. |
no_draft |
Publish | Re-open the designer to create a draft. |
publish_invalid |
Publish | Fix the listed tab/widget errors. |
invalid_default |
Default action | Publish and activate the dashboard first. |
invalid_definition |
Seeding | Fix the definition errors from dry-run. |
default_conflict |
Seeding | Resolve the other module default or use --force. |
unknown_wireframe |
Create/apply wireframe | Use a valid wireframe code. |
Full descriptions are in Dashboard Reference — Error Codes.
User Validation Checklist
The user is responsible for all validation. A reasonable manual pass:
- [ ] The report appears in
discover_dashboard_reports(). - [ ] The metadata endpoint returns JSON with the expected dimensions and measures.
- [ ] The report appears in the widget editor for the dashboard's module.
- [ ] A user with the output permission sees widget data.
- [ ] A user without it sees a permission error, not the rows.
- [ ] The dashboard is published, active, and visible at
/dashboards/<module>/. - [ ] Draft edits do not change the published page.
- [ ] Publishing validates and creates a new draft.
- [ ] Seeding is idempotent: a second run reports no duplicates.
- [ ]
--dry-runcatches an intentionally broken definition. - [ ] The module entry selects the expected preferred/default dashboard.
- [ ] Period and comparison controls change KPI and chart values as expected.
- [ ] Table paging and sorting work within configured limits.
Per project rules, the agent does not run tests and does not claim that any of these pass.