# Checking a model, and fixing what the report says axmo-format-version: ed0892bbbdf4 ## Running the check ``` node tool/axmo-check.mjs model.axmo # the report; exit 0 = no error, 1 = an error, 2 = the tool failed node tool/axmo-check.mjs model.axmo --values # … then every output range's cells node tool/axmo-check.mjs model.axmo --series "Cost" # … then one series, one line per coordinate node tool/axmo-check.mjs model.axmo --series "Cost" --at "Versions=Actual;Time=2027-Q1" # … only there node tool/axmo-check.mjs model.axmo --strict # a warning fails it too (exit 1): finish with this ``` - **Node 18 or later.** The tool runs the product's own import, compile and update, over a stand-in for Excel. - **`--series` on a large series is very long**, because it prints every coordinate, totals included. Narrow it with `--at`: `;` separates the pairs, and a dimension not named prints all its elements. - **The flags and exit codes** are in [`../generated/tool-contract.md`](../generated/tool-contract.md). - **If you cannot download it or run code**, use the other routes in [README](README.md). ## Reading the report A clean report is one line: `No issues.` Otherwise there are three sections, **Import**, **Compile** and **Update**, and each issue is one line: ``` error[FORMAT_UNKNOWN_KEY]: series.Sales.formla [12:5]: unknown key | expected one of: "type", "formula", "description", "dimensions" ``` - **The code** is in [`../generated/error-codes.md`](../generated/error-codes.md), with its meaning. - **The path and position** point into the file. - **The forms** are all in [`../generated/message-forms.md`](../generated/message-forms.md). - **Import judges the file's shape; Compile judges its meaning.** A file that names something missing still imports, and the compile says what is missing. **Warnings matter.** The import succeeds and the exit code is 0 even when a whole source loaded nothing, because every label failed to match. **Read every warning**, and finish with `--strict`, which turns a warning into exit `1` and ends the report with *(checked with --strict …)*. ## Common faults and their fixes | The report says | Usually because | Fix | |---|---|---| | `IMPORT_BUILD_REFUSED` … *has a layer named "X" identical to the dimension's own name* | Dimension `Account` with layer `Account` | Plural dimension, singular layer: `Accounts` / `Account`. | | `FORMAT_UNKNOWN_KEY` | A misspelt or invented key | Use a key the message lists. | | `DEFINITION_VALIDITY_ERROR` … *varies over dimension "Time" that is not in the output scope* | An output range shows a series without one of its dimensions | Put the dimension on the rows or columns, or fix it in the expression (`Sales[2027]`). | | `DG_PARENT_NOT_FOUND` … *parent "Retail" … is not in dimension* | A row names a parent that no generator and no written element supplies | Add the parent to the tree or to a generator's source, or correct the label. | | `DG_TARGET_SEQUENTIAL` | A generator targets a time dimension | Remove it; extend the time dimension's `start` / `end`. | | `SERIES_CELLS_FAILED` … *division by zero, in the roll-up formula for "Time"* | A `FORMULA` total that has no answer at some level (a previous month at a quarter) | `NONE` on that dimension, or `IFERROR` in the formula. | | `DIMENSIONAL_INCOMPATIBILITY` | A formula yields a dimension its series lacks | Read the total (`X[All_Products]`) or filter the dimension; or add the dimension to the series. | | `SL_DIM_MAPPING_MISMATCH` | A loader maps or fixes too few or too many dimensions | Map or fix exactly the target's dimensions. | | `OR_RENDER_RUNTIME_FAILED` | Cells of a report could not be calculated; the line names the first one (its series and elements) | Check that series' formula at those elements. If it says *Axmo hit an internal problem*, it is a defect: give the user the line to report. | | `FORMAT_VALUE_NOT_ALLOWED` | A key holds a value outside its set (`bandEndMode: bfore`) | Use a value the message lists. | | `SERIES_LOADER_CROSS_LOADER_CONFLICT` | Two loaders write the same cell; the line states both values, equal or not | Make the loaders' coordinates disjoint, whatever the values. | | `SERIES_LOADER_UNKNOWN_DIM_ELEMENT` on a wide table's `Total` column | The band runs to the end and takes in `Total` | `bandEndMode: before`, `bandEndColumn: Total`. | | `SERIES_LOADER_VALUE_ABOVE_LEAVES` | Values loaded above a dimension's leaves under split-down `NONE`, roll-up active: a series calculated at the leaves does not see them | A split-down on that dimension, or roll-up `NONE` too (a fully manual load). | | A warning per label, rows skipped | Labels in the data do not match the element names | Reshape the data to the element names (Axmo does not convert labels). | ## What Axmo cannot do today Say so to the user rather than working around it silently: - **No label conversion or mapping** ([01](01-method.md#3-reconcile-the-sources)). A dimension cannot be read from two columns. - **No fiscal calendar**, and no time naming other than `2027`, `2027-H1`, `2027-Q1`, `2027-01` and `2027-01-30`. - **A time dimension cannot be extended from data**, only by its parameters. - **Vector and scalar loaders load nothing.** Use tables. - **Calculated series are dense.** A table-mode output prints every combination. - **Re-importing a model replaces it** ([01](01-method.md#changing-an-existing-model)). The check tool has no workbook, so it never shows the **Import anyway** choice. - **Rows below a fixed-address input range** are neither loaded nor exported. Use an Excel Table, which grows. ## The workbook tool `tool/axmo-workbook.mjs` takes the model out of an Axmo workbook and puts a changed one back ([01](01-method.md#changing-an-existing-model) gives the flow; the contract is [`../generated/tool-contract.md`](../generated/tool-contract.md)): ``` node tool/axmo-workbook.mjs extract book.xlsx --out work --depth complete # work/model.yaml, work/dsdata.yaml node tool/axmo-workbook.mjs apply book.xlsx work/model.yaml -o book-v2.xlsx # exit 0 written, 1 refused, 2 failed ``` - **`apply` prints the check's report first.** An Import or Compile error refuses. **Its Update section never refuses, and is expected to show lines you must not fix**: the check has none of the workbook's cells or tables, so a source on a table reads `EXCEL_REFERENCE_UNRESOLVED`, and loaders find their sources empty (`SL_COLUMN_NOT_FOUND`, then `SERIES_CELLS_FAILED` and other lines from calculating on empty inputs). Then come `Apply` and its own lines. - **`--strict`** refuses on a warning among `apply`'s own lines, never on the check's Update section. - **`extract --depth complete`** adds the `data` section (cells as last saved; a formula as its value): check values on it, then apply it; its data is reported as not applied. | `apply` says | Meaning | What to do | |---|---|---| | `APPLY_SOURCE_NOT_IN_WORKBOOK` (refused) | A source's `rangeRef` names a sheet, range, table or name the workbook does not hold | Correct the reference to one the workbook holds. | | `APPLY_DSDATA_BROKEN` | A `DS.DATA` formula no longer compiles against the new model; the line gives its cell and the message | No formula is rewritten: tell the user which cell to edit, and to what. | | `APPLY_OUTPUT_RANGE_MOVED` / `APPLY_OUTPUT_RANGE_REMOVED` | A report moved or went; its old cells stay | Tell the user which cells to clear. | | `APPLY_DATA_NOT_APPLIED` | The file's `data` section was not written | Data changes in Excel, or through Import at `complete`. | | `APPLY_OUTPUT_SHEET_NOT_IN_WORKBOOK` | A report's sheet is not in the workbook; Update does not create it | Tell the user to add the sheet before pressing Update, or anchor the report on an existing sheet. | | `APPLY_LOADER_COLUMN_NOT_IN_WORKBOOK` | A loader names a column its source does not have in the workbook | Correct the column name. | | `WORKBOOK_OBJECT_LEFT_OUT` (`extract`) | A stored object is broken; the line gives its definition error, and it is not in `model.yaml` | Do not apply: that would drop it. The user repairs it in Excel, then extract again. | | `WORKBOOK_MODEL_NOT_WRITABLE` (`extract`, refused) | A stored value the file format has no word for | The user repairs the named field in the add-in, then extract again. | | `EXTRACT_FORMULA_NOT_CALCULATED` / `EXTRACT_DATA_NOT_READ` (`extract`) | A formula cell with no saved value, or a source the file does not resolve: written empty | Check those values; ask the user to open and save the workbook in Excel. | | `WORKBOOK_NOT_AXMO` (refused) | No model in the file: not an Axmo workbook, or saved by a library that removed it | Ask for the workbook as Axmo last saved it, or for an export. |