Troubleshooting and Reference

Diagnose common notebook, workbook, package, persistence, and custom-function problems, then use the compact public API reference for exact signatures and limits.

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, mixed row/scalar shapes, unsafe integers, unordered sets, unsupported Python objects, invalid rich-value structures, or oversized outputs. Ragged two-dimensional sequences are supported and padded with #N/A; NaN/±infinity map to #NUM!, pd.NA maps to #N/A, and pd.NaT maps to #NUM!.

Diagnostic: inspect the Python value before publication and compare it with the Excel ↔︎ Python type-conversion reference. Convert unsupported values explicitly to a scalar, vertical one-dimensional sequence, 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 reference Python value
One cell Typed scalar: blank → None, explicit empty text → "", formatted date/time → Python date/time value
Multi-cell range pandas DataFrame; text columns normalize to object, date/datetime columns to datetime64[ns]
Multi-cell range with headers=True DataFrame with first row used as columns
Worksheet error Typed ExcelErrorValue rather than plain error-text string

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 → vertical spill;
  • rectangular two-dimensional sequence → spill range;
  • ragged two-dimensional sequence → rectangular spill padded with #N/A.

Names are exact and case-sensitive. An absent name returns an invalid-name error after the notebook has completed startup. See Excel ↔︎ Python type conversion for the exhaustive result contract.

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:

  • int/float, str, bool, and None;
  • Python-in-Excel-compatible missing/error mappings: NaN/±infinity → #NUM!, pd.NA#N/A, pd.NaT#NUM!, complex → #N/A;
  • one-dimensional vertical spills and rectangular two-dimensional sequences;
  • ragged two-dimensional sequences padded with #N/A;
  • pandas Series and DataFrame, including headings and the Python-in-Excel index-layout rules;
  • NumPy scalars and arrays, including the tested 0-D and 3-D error behavior;
  • Python date, naive datetime, time, and timedelta; timezone-aware date/time values return #VALUE!;
  • supported Excel rich-value dictionaries.

Unsafe integers, unordered sets, empty sequences, mixed row/scalar shapes, unsupported objects, invalid rich-value payloads, and oversized results return deterministic worksheet errors.

For the complete table and the executable comparison workbook, see Excel ↔︎ Python type conversion.

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

Limit Value
Named inputs 128
Reference text 512 characters
Cells per input reference 100,000
Aggregate input cells 250,000
Published outputs 128
Published functions 128
Cells per published value/function result 100,000
Cells across one function call’s arguments 100,000
Active worksheet function subscriptions 512
Concurrent Python function executions 8
Queued Python function executions 128
Function timeout 60 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.