Skip to main content

Troubleshooting and Reference

Start with the symptom if something is failing. The second half of this page provides the exact public Boardflare notebook and worksheet API contract for advanced use.

Troubleshooting

Use this page as symptom → likely cause → diagnostic → recovery. When a workbook fails, first determine whether the failure is in startup/persistence, workbook input binding, publication, a package/network dependency, or the workbook's own business logic.

BF.OUTPUT() or BF.FUNCTION() shows #BUSY!

Likely cause: the shared runtime, notebook, workbook inputs, or publication registry is still starting.

Diagnostic: open the Notebook tab and check whether the notebook is still initializing, has a startup error, or never reached the displayed bf.publish() cell.

Recovery: wait for normal cold start. If it does not resolve, retry/restart the notebook session. Do not replace #BUSY! with a placeholder workbook result; Boardflare intentionally keeps Excel's streaming invocation pending until the registry is available or startup fails terminally.

Notebook initialization timed out / failed to start

Likely cause: the notebook frontend did not become ready within its startup window, or the pinned export/source/helper assets failed to load.

Diagnostic: inspect the Notebook recovery/status surface and browser/add-in console if you are developing the product itself. A user workbook should not depend on private marimo internals.

Recovery: retry the session. Use reset only when discarding the saved source is acceptable. A reset is not the first response to a transient network/startup error.

If the failure started immediately after uploading a Marimo .py file, use Restore saved notebook. Uploaded source is staged in memory until a successful Save, so this recovery returns to the last saved workbook source instead of deleting it.

An uploaded .py file is rejected

Likely causes: the file is not a .py file, is empty, is not valid UTF-8, or exceeds the 200,000-byte notebook source limit.

Recovery: export/save the notebook as UTF-8 Marimo Python source, reduce it below the workbook source limit if needed, then upload again. Loading the file starts a replacement Edit session but does not make it durable until Marimo Save reaches Boardflare Saved status.

Notebook AI controls are missing

Likely cause: Notebook AI requires an eligible work or school Microsoft identity. Personal Microsoft accounts and sessions without a resolved eligible Office identity receive Marimo with AI disabled.

Diagnostic: confirm that the add-in is signed in with the intended organizational account. Do not use the presence of BF.OUTPUT()/BF.FUNCTION() results as an authentication test; calculation startup is intentionally independent of Notebook AI eligibility.

Recovery: sign in with an eligible work or school Microsoft account when AI authoring is required. The workbook calculation and saved notebook remain usable without Notebook AI. See AI Authoring and Security and Data Flow.

A workbook input is not updating

Likely causes: the bf.inputs() widget is not displayed, the workbook reference is wrong, the expected table shape changed, or downstream cells do not actually read the input mapping.

Diagnostic:

inputs.errors

Confirm the binding name and canonical reference shown by the input widget, then verify downstream code reads inputs["name"].

Recovery: restore the displayed widget, correct the reference/worksheet structure, or fix validation logic. A replacement input model does not take ownership until its first snapshot succeeds, so the prior working binding can remain active during a failed replacement attempt.

An input raises after startup

Likely cause: the host returned an error for that binding (invalid/oversized/unavailable reference or another materialization problem).

Diagnostic: inspect inputs.errors and the widget status rather than catching the failure and continuing with stale data.

Recovery: correct the workbook reference/data problem. Boardflare deliberately raises on a failed input after initialization so downstream cells do not silently use a placeholder snapshot.

BF.OUTPUT() says the output is unavailable/unknown

Likely causes: spelling/case mismatch, the current bf.publish(outputs=...) registry does not contain that key, publication failed, or the notebook is stopped.

Diagnostic: compare the formula name exactly with the currently displayed publication cell:

publication = bf.publish(outputs={"summary": summary})
publication
=BF.OUTPUT("summary")

Recovery: fix the name or publication error and let the successful generation claim the registry. Saving source does not freeze the current output value into workbook storage.

When the publication is assigned to a variable, publication.consumers can help confirm which live worksheet formulas are connected to the current output/function registry.

BF.FUNCTION() fails

Likely causes: unknown name, wrong positional argument count, unsupported function signature, Python exception, result-conversion error, timeout, or a disconnected publication widget.

Diagnostic: confirm the exact function mapping and signature:

def discount(price, rate=0.0):
...

publication = bf.publish(functions={"discount": discount})
publication

Check that the worksheet formula supplies required positional arguments and that the function returns a supported scalar/table value.

Recovery: fix the signature/arguments/error and republish. Avoid required keyword-only parameters and **kwargs; worksheet calls are positional.

A function remains busy or times out

Likely cause: the Python callable is long-running, especially a synchronous function that blocks the marimo kernel.

Diagnostic: call the underlying Python function from the notebook with representative arguments and measure/inspect its behavior.

Recovery: move expensive work into reactive model state and publish it with BF.OUTPUT() when possible. Keep BF.FUNCTION() for short calculations. Timeout/cancellation can suppress stale worksheet results but cannot force-preempt synchronous Python that is already executing.

Published output is malformed or returns a value error

Likely cause: the Python result cannot be converted to a rectangular worksheet value.

Common unsupported results include empty sequences, ragged matrices, mixed row/scalar shapes, positive/negative infinity, unsafe integers, unsupported Python objects, invalid rich-value structures, or oversized outputs. None, pandas missing values, and floating-point NaN are valid result-cell values and become blank worksheet cells.

Diagnostic: inspect the Python value before publication and convert it explicitly to a scalar, one-dimensional row, rectangular two-dimensional sequence, pandas object, or supported NumPy/date value.

Recovery: normalize the result before bf.publish() or before returning it from BF.FUNCTION().

Package installation/import fails

Likely cause: the package or one of its dependencies lacks a Pyodide/WebAssembly-compatible distribution.

Diagnostic: check the Pyodide package list and package-loading documentation. Do not assume a desktop wheel works in the browser.

Recovery: use a compatible/pure-Python alternative, install a compatible wheel with micropip, or move the workload to external/managed Python.

API/network request fails

Likely cause: CORS, authentication, browser CSP/policy, unsupported socket behavior, or the remote service is unavailable.

Diagnostic: determine whether the request can be made from a normal browser client. A URL working from desktop/server Python is not sufficient.

Recovery: use a browser-compatible API/auth flow, a server-side proxy you control, or external Python. Do not weaken workbook security expectations to make an incompatible API callable.

Changes disappeared after reopening

Likely cause: the edits were never saved through the notebook save flow, save verification failed, or the changes were made only in a session-backed browser demo.

Diagnostic: use Boardflare's save status, not only marimo's editor state, as the persistence signal.

Recovery: reopen the source, save until Boardflare reports success, then close/reopen and verify. In the web demo, understand that session/demo persistence is not the same as Excel workbook Custom XML.

Changing startup mode is blocked

Likely cause: the notebook has unsaved source. Boardflare does not persist a new startup preference against source that has not been committed.

Recovery: save the notebook first, then change the saved Edit/App presentation.

The web demo behaves differently from Excel

Cause: the standalone demo uses Univer as its spreadsheet host. It shares Boardflare source/input/output protocols where practical, but source persistence and custom-function lifecycle differ.

Recovery: use the web demo for exploration and authoring patterns, then validate final delivery in Excel: save/reopen, streaming BF.OUTPUT(), streaming BF.FUNCTION(), and second-user startup.

mo.stop / ancestor-stopped messages appear in the console

Marimo can intentionally stop a cell before its required data is ready. Descendant cells then report that they were not run because an ancestor stopped.

Boardflare's input widget uses this behavior during initial hydration so downstream calculations do not publish placeholder results before the first workbook snapshot arrives. If the notebook later hydrates and runs normally, these messages are expected runtime diagnostics rather than notebook failures.

Investigate them when the raising cell never becomes ready or is stopped by notebook logic you did not expect.

Workbook API

Use the public boardflare package from notebook code:

import boardflare as bf

The top-level notebook API intentionally contains only:

  • bf.ref(reference, headers=False)
  • bf.inputs(**named_references)
  • bf.publish(outputs=..., functions=...)

Internal bridge objects, Anywidget model classes, iframe protocol objects, and marimo private APIs are not part of the public contract.

Public names

Input names, published output names, and published function names are:

  • case-sensitive;
  • required to begin with an ASCII letter;
  • limited to ASCII letters, digits, and underscores after the first character;
  • limited to 128 characters.

Use names that remain clear when they appear in worksheet formulas such as BF.OUTPUT("forecast").

bf.ref(reference, headers=False)

Describe a workbook reference when options are needed:

bf.ref("Sales!A1:D20", headers=True)

A plain string passed to bf.inputs() is equivalent to bf.ref(reference).

Supported reference forms are resolved by the spreadsheet host and include A1 ranges, sheet-qualified ranges, and defined names. Prefer sheet-qualified references in multi-sheet workbooks:

bf.ref("Drivers!A4:B12", headers=True)

When headers=True, the first row supplies DataFrame column names and is removed from the returned data rows.

bf.inputs(**named_references)

Declare the notebook's reactive workbook dependencies:

inputs = bf.inputs(
sales=bf.ref("Sales!A1:D20", headers=True),
tax_rate="Assumptions!B2",
)
inputs

Display the returned widget as the cell result. Read values through mapping access:

sales = inputs["sales"]
tax_rate = inputs["tax_rate"]

Attribute-style access such as inputs.sales is not the public input-value interface. Anywidget reserves attribute space for model/runtime behavior, while mapping keys are the user's input namespace.

Materialization rules

Workbook referencePython value
One cellScalar
Multi-cell rangepandas DataFrame
Multi-cell range with headers=TrueDataFrame with first row used as columns
Empty workbook cellMay materialize as None

Before the first workbook snapshot arrives, reading an input stops the current marimo cell and its descendants. Marimo reruns those cells when the input widget becomes ready. This prevents the notebook from publishing placeholder calculations during startup.

Reactivity and errors

Workbook changes are filtered against resolved dependency ranges where possible and debounced before refresh. The host publishes one complete atomic snapshot containing values, canonical references, and per-input errors.

Inspect failures through:

inputs.errors

After initialization, reading a failed input raises rather than silently returning a placeholder value.

A replacement input widget does not displace the prior working generation until its first snapshot succeeds.

bf.publish(outputs=None, functions=None)

Publish the complete live value/function registry:

def discount(price, rate=0.0):
return float(price) * (1 - float(rate))

publication = bf.publish(
outputs={"summary": summary},
functions={"discount": discount},
)
publication

Display the returned widget so its Anywidget model remains connected.

Publication is generation-based: the new complete registry is validated and converted before it claims ownership. A failed candidate does not partially replace the previous successful publication.

Published outputs/functions are live-session state. Saving stores notebook source that recreates them; it does not serialize the current Python objects.

The returned publication widget also exposes a read-only publication.consumers mapping with the worksheet formulas currently consuming published outputs and functions. In Excel these references are reported by formula anchor cell; the mapping is live runtime state and is not saved with the workbook.

BF.OUTPUT(name)

Read a published value from Excel:

=BF.OUTPUT("summary")

BF.OUTPUT is streaming in Excel. During notebook or publication startup, Boardflare leaves the invocation pending so Excel can show its native #BUSY! state instead of publishing a temporary value.

Results are normalized to a worksheet matrix:

  • scalar → one cell;
  • one-dimensional sequence → one spill row;
  • rectangular two-dimensional sequence → spill range.

Names are exact and case-sensitive. An absent name returns an invalid-name error after the notebook has completed startup.

BF.FUNCTION(name, ...)

Invoke a function from the live published function registry:

=BF.FUNCTION("discount", A1, B1)

Function lookup uses the explicit mapping passed to bf.publish(). The runtime does not use eval, decorators, global discovery, arbitrary attributes, import paths, or persisted executable catalogs.

Supported signatures

Supported callables include:

def required(a, b):
...

def optional(a, b=10):
...

def variable(a, *items):
...

async def async_lookup(code):
...

The runtime supports positional-only parameters, positional-or-keyword parameters, trailing defaults, optional *args, synchronous functions, asynchronous functions, and optional keyword-only parameters that have defaults.

Rejected shapes include required keyword-only parameters, **kwargs, and invalid positional/default ordering.

Worksheet calls remain positional.

Worksheet arguments

  • scalar worksheet values remain scalars;
  • a one-cell range is unwrapped to a scalar;
  • a multi-cell range becomes a two-dimensional list;
  • omitted trailing optional arguments use Python defaults;
  • omitted middle optional arguments are tracked separately so later arguments do not shift left.

Results

Supported result families include:

  • finite int/float, str, bool;
  • None, pandas missing values, and floating-point NaN as blank cells (including a top-level scalar);
  • one- and two-dimensional sequences;
  • pandas Series and DataFrame;
  • NumPy scalars and one- or two-dimensional arrays;
  • Python date, datetime, time, and timedelta;
  • supported Excel rich-value dictionaries.

Unsupported objects, empty/ragged matrices, unsafe integers, positive/negative infinity, invalid rich-value payloads, and oversized results return deterministic worksheet errors.

Keep synchronous functions short. A timeout suppresses a stale worksheet result but cannot force-preempt Python code that is already blocking the marimo kernel.

Practical limits

LimitValue
Named inputs128
Reference text512 characters
Cells per input reference100,000
Aggregate input cells250,000
Published outputs128
Published functions128
Cells per published value/function result100,000
Cells across one function call's arguments100,000
Active worksheet function subscriptions512
Concurrent Python function executions8
Queued Python function executions128
Function timeout60 seconds

Common anti-patterns

Do not use stale/private patterns such as:

# Not the public notebook API
bf.range("A1:B2")
bf.outputs(...)
inputs.sales

and do not write worksheet formulas such as:

=BF.VALUE("summary")

The current public worksheet functions are BF.OUTPUT() and BF.FUNCTION().

API stability

The names and behavior on this page are the supported notebook contract. Do not depend on marimo private runtime modules, Boardflare iframe messaging internals, or internal spreadsheet bridge objects from user notebooks.