# The errors every Tableau extension throws

> Blank box, no message, FD722608, an extension that dies once published. The Tableau extension errors you will actually hit, with the cause and fix for each.

Canonical: https://ext.tableauops.com/knowledge/tableau-extension-errors
Updated: 2026-09-18
Author: Eric Summers


Tableau's extension errors are terse to the point of rudeness. Most are one line, several are a blank rectangle where your extension should be, and one is a hex code. They are also finite. Here are the ones you will actually hit, what each really means, and the fix. If you paste an error into an AI assistant, paste the exact text: these messages are terse but specific, and the specificity is the only thing that makes them fixable. For the machine-readable code table, see [the error code reference](/knowledge/extensions-api-error-codes).

## My manifest change did nothing

You edited the `.trex`, reloaded, and Tableau is still running the old one.

HTML and JS hot-reload. Manifests do not. When you add an extension, Tableau reads the `.trex` once and embeds a copy in the workbook, and the file on disk stops mattering. For HTML or JS edits, reload the extension. For any manifest change, remove and re-add it. This one disguises itself as every other error on this list: you fix a genuine bug, see the identical failure, and conclude the fix did not work, when Tableau never read it.

## It loads, but it is blank

The object is there, it has a border, it contains nothing. Usually the dashboard has no worksheet with a measure yet, or your code is reading a worksheet that is not the one you think.

Put a worksheet with a measure on the dashboard. In code, find the worksheet and the measure by name or type, never by hard-coded index. The deeper fix is to render a real empty state so a missing input is never indistinguishable from a broken extension:

```js
if (!worksheet || !measures.length) {
  show('Add a worksheet with a measure to this dashboard');
  return;
}
```

## Missing XML element

The manifest will not parse at all: a required element is missing. This is the most common first-load error, and it is almost always a hand-written or AI-generated manifest that looked complete.

Keep a known-good manifest in the repo and diff against it. The required set, in order: `default-locale`, `name`, `description`, `author`, `min-api-version`, `source-location`, `icon`. Order matters, the schema is a sequence, not a set. See [what is inside a .trex](/knowledge/whats-in-a-trex).

## It will not load, just spins

No error, no content, no timeout, just a spinner forever. The cause is port contention: more than one extension project running on local servers, and the manifest's `url` points at a port now serving something else or nothing.

One project, one port. Shut down every other local server, confirm the port in the manifest is the port you are serving from, restart clean. There is no message anywhere because from Tableau's point of view nothing has gone wrong yet; it is still waiting. The reliable habit: before debugging anything else, open the manifest's URL in a plain browser tab. If it does not render there, it will never render in Tableau, and the problem is your server.

## Data reads empty when there is clearly data

The worksheet is full of numbers and your extension sees none. You are reading a column by index. `getSummaryDataAsync` returns columns in pill order, which changes every time the user rearranges the sheet.

Find columns by `dataType` (`'float'` / `'int'`) or by name. Use `nativeValue` for arithmetic; `formattedValue` is a display string and will quietly poison your maths.

```js
// Breaks the moment someone reorders the pills.
const total = rows.reduce((a, r) => a + r[2].nativeValue, 0);

// Survives it.
const m = table.columns.find(c => c.dataType === 'float' || c.dataType === 'int');
const total = rows.reduce((a, r) => a + r[m.index].nativeValue, 0);
```

See [reading worksheet data](/knowledge/reading-worksheet-data) for the full pattern.

## FD722608: the content-model error

`missing elements in content model '(… source-location,icon,permissions?,…)'`. Your manifest has no `<icon>` element. Some Tableau builds list it as required (note there is no `?` after `icon` in that content model, while `permissions` has one), so leaving it out fails to parse.

Add an empty `<icon/>` immediately after `</source-location>`. No attributes, no content:

```xml
  <source-location>
    <url>https://your-host.example.com/index.html</url>
  </source-location>
  <icon/>
</dashboard-extension>
```

Learning to read that message is worth more than the fix, because the same code covers a family of problems. The parenthesised list is the schema: elements in required order, a `?` marking the optional ones. Whatever is in that list and missing from your file is your answer.

FD722608 has two other common triggers. A `<resources>` block nested inside the extension root instead of as a sibling produces `no declaration found for element 'resources'`, surfaced under the same code. And a double hyphen (`--`) inside an XML comment is illegal XML: `'--' sequence is illegal in comment`, classic when you paste a CLI flag like `--bind` into a comment. Write flags in prose.

## ED626076: two encodings share an icon

Building a viz extension, two `<encoding>` blocks use the same `encoding-icon token` (two measures both `metric`, two dimensions both `level-of-detail`). Tableau refuses on add with `Cannot use the same icon for more than one encoding`, and a workbook that embeds the manifest fails to open.

Give every tile a distinct token from Tableau's enumeration. Before shipping, grep for duplicates: `grep -o 'encoding-icon token="[^"]*"' manifest.trex | sort | uniq -d`. Note the published docs list tokens the parser rejects (like `line` and `detail`), and it reports only the first bad one, so you fix and re-fail several times. See [encoding icon tokens](/knowledge/encoding-icon-tokens).

## min-api-version too low

Your code calls a newer Extensions API method and the extension fails on a version of Tableau that predates it. `min-api-version` is a compatibility floor, not a target. Raising it locks out older Tableau, but setting it below what your code needs lets the extension load into a Tableau that lacks the method. Set the floor to the oldest API your code truly needs, and type-of-guard newer features at runtime rather than raising the floor for them.

## Fine on desktop, broken once published

It works in Tableau Desktop and is an empty rectangle on Server or Cloud. Either the `.trex` still points at `localhost`, or the URL is not on the site's safe list. Desktop is far more permissive than Server, and that gap is the whole error.

Ship the production manifest pointing at a real HTTPS URL, and have a site admin add that URL to the extension safe list. Tableau will not load a production extension over plain `http`, and self-signed certificates fail too. One related non-bug: extensions do not render in PDF exports or printed images on Server and Cloud. Blank space where the extension was is expected, not something you fix in code.

## FAQ

### Is FD722608 always a missing icon?

No. It is the code for a whole family of content-model and parse problems: a missing `<icon/>`, elements in the wrong order, a `<resources>` block nested wrongly, or a `--` inside an XML comment. Read the parenthesised content model in the message and compare it against your file in order.

### Why does my extension work on Desktop but not on Server?

Desktop is more permissive. On Server or Cloud the `.trex` must point at a real HTTPS URL and a site admin must add that URL to the extension safe list. A manifest still pointing at `localhost` is the other common cause.

### nativeValue or formattedValue for calculations?

Always `nativeValue`. `formattedValue` is a display string (with currency symbols, thousands separators, rounding) and parsing it as a number gives wrong totals. Use `formattedValue` only for what you print to the screen.

### When should I stop patching and rewrite?

If you are three iterations deep on the same error with no movement, regenerating that piece from a clean prompt is usually faster than a fourth patch. State the goal, the field setup, and what is failing. Patching a misunderstanding just layers more code on top of it.


---
Try it live: Score your workbook — https://ext.tableauops.com/roast
