# Troubleshoot an extension

> What an extension on Toolaby says when something is wrong, why, and what fixes it.

The client checks the extension's manifest when its service worker starts, and says what is missing in the service worker's console, prefixed `[toolaby]`, with the lines to add. Open it at `chrome://extensions` → your extension → **Service worker**.

This page lists those messages, the messages `wire` stops with, then the problems that show elsewhere: in the popup, on Toolaby's pages, and on the Chrome Web Store.

`npx -y toolaby@latest check` finds most of these before the extension runs. Each of its findings has a code, such as `CSP_BLOCKS_TOOLABY`; the rows below name them. [Command line](https://toolaby.app/docs/cli.md#check-folder) lists every code.

## At startup

| Message                                                                                                               | Cause                                                                                                                                                                                                                                                             | Fix                                                                                                                                                                                                                                                                     |
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ``Toolaby(config): `key` must be a tool key from the platform — tk_test_… or tk_live_…``                              | `toolaby.config.js` still holds the placeholder, or no key at all. The extension does not start.                                                                                                                                                                  | `npx -y toolaby@latest wire <tool-id>` in the extension's folder, or paste the key from the foot of the tool's **Set up** page: **Your test key** or **Your live key**, then **Show**.                                                                                  |
| `Toolaby(config): the tool key is damaged — copy it again from the tool's Set up page`                                | The key was cut short when it was copied.                                                                                                                                                                                                                         | Copy it again from the foot of the tool's **Set up** page: **Your test key** or **Your live key**, then **Show**.                                                                                                                                                       |
| `[toolaby] The "storage" permission is missing.`                                                                      | The manifest has no `storage` permission. Nothing the client keeps survives.                                                                                                                                                                                      | Add `"permissions": ["storage"]`. `npx -y toolaby@latest wire <tool-id> --force` does it.                                                                                                                                                                               |
| `[toolaby] externally_connectable does not include https://…/*.`                                                      | Sign-in and the after-purchase page cannot hand anything to the extension.                                                                                                                                                                                        | `npx -y toolaby@latest wire <tool-id> --force`, or add the line the message shows.                                                                                                                                                                                      |
| `[toolaby] manifest_version is …; the client is written for Manifest V3 service workers.`                             | The manifest is not Manifest V3.                                                                                                                                                                                                                                  | Move the extension to Manifest V3. See [When `wire` stops](#when-wire-stops).                                                                                                                                                                                           |
| `[toolaby] This build was installed from a store but its tool key points at …`                                        | A copy installed from a store carries a key whose address is `http://localhost` or `http://127.0.0.1`: a development server's.                                                                                                                                    | `npx -y toolaby@latest wire <tool-id> --live --force`, build, `npx -y toolaby@latest pack`, upload. See [Ship to the Chrome Web Store](https://toolaby.app/docs/ship.md).                                                                                                                     |
| `[toolaby] This build was installed from a store but carries the Test key (tk_test_…)`                                | The store copy carries the Test key: its buyers pay on Test, with test cards, and nothing unlocks on Live. `npx -y toolaby@latest check` reports `TEST_KEY_IN_LIVE_BUILD`: an error for a copy whose manifest has `update_url`, a warning for a production build. | `npx -y toolaby@latest upgrade <tool-id> --live`, build, `npx -y toolaby@latest pack`, upload. `pack` refuses a Test key. See [Ship to the Chrome Web Store](https://toolaby.app/docs/ship.md).                                                                                               |
| `[toolaby] The content security policy (content_security_policy.extension_pages) leaves https://… out of connect-src` | The manifest limits what the extension may connect to, and leaves Toolaby's address out. Every call to Toolaby fails with *Failed to fetch*. `check` reports `CSP_BLOCKS_TOOLABY`.                                                                                | `npx -y toolaby@latest upgrade <tool-id>` adds the address to `connect-src`. In WXT, the module `upgrade` writes adds it at build time. In a CRXJS `manifest.config.ts`, wrap your policy: `extension_pages: cspWithToolaby("…")`, imported from `toolaby.manifest.js`. |

## When `wire` stops

`npx -y toolaby@latest wire` writes nothing when it stops. It prints one of these messages, with the folder, the file or the id in place of the angle brackets.

| What you see                                                                                           | Cause                                                                                                                                                                                                                                                          | Fix                                                                                                                                                                                                       |
| ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<folder> has no manifest.json at the top and is not a WXT, CRXJS or Plasmo project`                   | The folder has no `manifest.json` at its top, and its `package.json` names none of the three. When a manifest is below the top, the message names it and suggests `--into`.                                                                                    | Run it in the extension's folder. For a manifest below the top, use `--into` only when nothing builds the background it names; otherwise wire by hand. See [Frameworks](https://toolaby.app/docs/frameworks.md#other-bundlers). |
| `This folder's manifest names the extension <id>; <tool-id> is bound to <id>.`                         | The manifest carries the `key` of another extension than the one the tool is bound to.                                                                                                                                                                         | Run it again with `--adopt-key` to make the folder's extension the tool's. Or take the `key` out of the manifest, and the tool's own goes in.                                                             |
| `No tool "<tool-id>" in your workspaces.`                                                              | The id is wrong; the short id names a tool in more than one of your workspaces; your role in that workspace cannot configure tools; or the tool is in a workspace of another account. The message says which account you are signed in as, and its workspaces. | `npx -y toolaby@latest tools` lists your ids: use the full one. For another account: `npx -y toolaby@latest logout`, sign in to the dashboard as that account, then run it again.                         |
| `Refused: …` and &#x2A;This command writes only inside `<folder>`, and never through a symbolic link.* | A path the wiring would write leads out of the folder: an absolute path, a `..`, or a symbolic link at any step.                                                                                                                                               | Make each step a plain folder inside the extension's folder, and name the background in the manifest by a path inside it.                                                                                 |
| `toolaby.background.js is already in <folder>, and it is not Toolaby's.`                               | A file of yours has the name of Toolaby's background.                                                                                                                                                                                                          | Rename your file, then run it again.                                                                                                                                                                      |
| `"<file>", the background the manifest names, is already in <folder>; it would have been replaced.`    | The manifest spells the background another way than the folder does, or a build writes it.                                                                                                                                                                     | Name it in the manifest as the folder spells it. When a build writes it, run with `--into` the folder whose `manifest.json` names it.                                                                     |
| `<files> already in <folder>; add --force to replace them.`                                            | The folder was wired before.                                                                                                                                                                                                                                   | `npx -y toolaby@latest upgrade <tool-id>` replaces Toolaby's files; yours stay.                                                                                                                           |
| `This command (<version>) is older than Toolaby`                                                       | An installed command expects files Toolaby no longer hands out.                                                                                                                                                                                                | Run `npx -y toolaby@latest wire <tool-id>`, which is always the current version, or update the installed one with `npm install -g toolaby@latest`.                                                        |
| `<folder> does not exist`                                                                              | `--into` names a folder that is not there.                                                                                                                                                                                                                     | Check the path.                                                                                                                                                                                           |
| A `manifest.json` with comments                                                                        | Chrome reads `//` and `/* */` comments in `manifest.json`.                                                                                                                                                                                                     | Nothing to do: `wire` reads them too, and writes the manifest back as plain JSON, without them, and says so.                                                                                              |
| A Manifest V2 extension (`manifest_version: 2`)                                                        | Chrome runs Manifest V3 extensions only.                                                                                                                                                                                                                       | `wire` stops and writes nothing. Move the extension to Manifest V3 first: see [Chrome's migration guide](https://developer.chrome.com/docs/extensions/develop/migrate).                                   |

## In the extension

| What you see                                                                                                                                    | Cause                                                                                                                                                                                                                                                                                       | Fix                                                                                                                                                                                                                                                                                                         |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The sign-in page says **Connected**, and the extension still says *Not signed in*                                                               | The manifest's `externally_connectable` lacks Toolaby's address, so the page could not reach the extension.                                                                                                                                                                                 | See the startup message above. Then sign in again.                                                                                                                                                                                                                                                          |
| Toolaby answers `403 forbidden_origin`: &#x2A;This tool key is bound to another extension. On the tool's Set up page, choose this one instead.* | Once you name your extension, a tool key works from the store copy, the copies with the wiring's `key`, and the copy you chose on the **Set up** page. This is another.                                                                                                                     | On the tool's **Set up** page, **Use this copy instead** beside its id. Keep the manifest's `key` so every copy you load has one id.                                                                                                                                                                        |
| The popup says **Not set up yet**, or a method rejects with *Toolaby is not running in this extension's background*                             | Nothing starts Toolaby in the background: the manifest names a background of yours that never calls it. Chrome says *Receiving end does not exist*.                                                                                                                                         | **Wire again** on the Set up page, or `npx -y toolaby@latest upgrade <tool-id>`. Then reload the extension. `npx -y toolaby@latest check` names the background (`BACKGROUND_NOT_STARTED`); `check --browser` asks it.                                                                                       |
| A page of yours breaks where it reads the popup, or your popup opens without the check                                                          | Your code calls `chrome.action.getPopup()` or `setPopup()`, and finds or replaces the Toolaby popup in front of yours.                                                                                                                                                                      | **Keep my popup in front** on the Set up page, or `npx -y toolaby@latest upgrade <tool-id> --own-popup`. `toolaby.showPaywall()` then opens the Toolaby popup.                                                                                                                                              |
| A change in the dashboard does not show in the extension                                                                                        | Installed copies ask Toolaby at intervals: within seconds on Test; on Live at most once in five minutes when the Toolaby popup opens, and hourly otherwise. Code of yours that swaps the popup (`setPopup()`) skips the Toolaby popup altogether.                                           | Wait the interval. Keep your popup in front and gate your paid action (see the row above).                                                                                                                                                                                                                  |
| On Windows, `toolaby …` opens `toolaby.js` in your editor instead of running                                                                    | The Command Prompt runs a `toolaby.js` in the current folder before a command named `toolaby`, and every wired folder has one.                                                                                                                                                              | Run `npx -y toolaby@latest …`, or the installed command, `toolaby-cli`.                                                                                                                                                                                                                                     |
| Every open of the popup spends a free use                                                                                                       | The Toolaby popup stands in front of yours and the Free plan counts uses.                                                                                                                                                                                                                   | Put your popup first and count at the action. See [Gate your extension](https://toolaby.app/docs/gating.md#1-choose-what-opens-first).                                                                                                                                                                                            |
| `has()` answers yes for every feature                                                                                                           | The tool has no features under **Pricing**. A tool with none holds everything.                                                                                                                                                                                                              | Add the features, and tick them on the plans that hold them.                                                                                                                                                                                                                                                |
| `showPaywall()` opens a page that is not found                                                                                                  | `toolaby-popup.html` or `toolaby-sidepanel.html` is not in the build.                                                                                                                                                                                                                       | Keep both in the extension; the wiring writes them. A framework must copy them into its output. See [Frameworks](https://toolaby.app/docs/frameworks.md).                                                                                                                                                                         |
| The paywall opens at popup width in a side panel                                                                                                | The page does not say it is a side panel.                                                                                                                                                                                                                                                   | `<html data-toolaby-surface="sidepanel">`.                                                                                                                                                                                                                                                                  |
| The popup opens blank, and its console says *Cannot use import statement outside a module*                                                      | A page loads a script with `import` lines, such as `import { toolaby } from './toolaby.js'`, without `type="module"`. Chrome stops the script. `check` reports `POPUP_NOT_MODULE`, with the page and the line.                                                                              | Load it as a module: `<script type="module" src="popup.js"></script>`.                                                                                                                                                                                                                                      |
| The paywall never appears                                                                                                                       | Nothing in the extension can ask anyone to pay. The Free plan holds the tool's use (access is open) and the code gates no feature a plan sells; or your popup stands in front and the code never calls `gate()`. `check` reports `NO_PAYWALL_POSSIBLE`: an error when the tool sells plans. | Gate the paid action: `const permit = await toolaby.gate({ feature: '<key>' }); if (!permit.allowed) return toolaby.showPaywall(permit);`. Or let the Free plan hold a number of uses: `npx -y toolaby@latest access <tool-id> --access free_uses --free-uses 10`. See [Gate your extension](https://toolaby.app/docs/gating.md). |
| A purchase went through and the extension still shows the Free plan                                                                             | The purchase was made signed out, so it waits on the address it was made with. Or the after-purchase page could not reach the extension (see `externally_connectable`).                                                                                                                     | The buyer signs in once with that address: the after-purchase page's button, or **Sign in** in the extension. Then it unlocks.                                                                                                                                                                              |
| *Update needed* in the popup                                                                                                                    | The tool's **Versions** set a minimum above this build's version.                                                                                                                                                                                                                           | Publish a newer build, or lower the minimum.                                                                                                                                                                                                                                                                |

## In Chrome

| What you see                                                     | Cause                                                                                                        | Fix                                                                                                                      |
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| Chrome refuses to load the extension                             | The manifest's `side_panel.default_path` names a file that is not there. Chrome refuses the whole extension. | Put the file back, or remove the side panel with its key and the `sidePanel` permission. See [Surfaces](https://toolaby.app/docs/surfaces.md). |
| The extension's id changes when it is loaded from another folder | The manifest has no `key`.                                                                                   | `npx -y toolaby@latest wire <tool-id> --force` writes the tool's identity as the manifest's `key`.                       |

## On the Chrome Web Store

| What you see                                          | Cause                                                                                         | Fix                                                                       |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| *key field is not allowed in manifest*                | A new item's first package may not carry a `key`.                                             | Upload the zip `npx -y toolaby@latest pack` makes: it leaves the key out. |
| The upload is refused for its key on an update        | The package's `key` is not the item's own.                                                    | The same: `npx -y toolaby@latest pack`.                                   |
| The store copy is refused with `403 forbidden_origin` | You chose another copy on the **Set up** page, and the tool does not know the store copy yet. | Paste the listing's address under the tool's **Settings → Store**.        |

## See also

* [Gate your extension](https://toolaby.app/docs/gating.md)
* [Ship to the Chrome Web Store](https://toolaby.app/docs/ship.md)
* [Testing](https://toolaby.app/docs/testing.md)
