Skip to content

Troubleshooting

Ordered by when you hit it. Every entry leads with the symptom as you will experience it, because that is what you will be searching for.

The entries worth reading before you need them are the first three: each fails silently, producing a wrong result with no error anywhere.

A typed-data query returns nothing, and nothing errors

Section titled “A typed-data query returns nothing, and nothing errors”

You wrote a filter that looks obviously correct, the call succeeds, and the result is an empty array — with the rows plainly there.

// Silently matches zero rows.
const recent = await ctx.api.data.find('notes', {
where: { createdAt: { gte: dayAgo } },
});

Cause. A predicate has to be built, not described. An object with no operator key is not recognized as a predicate, so it is coerced for the comparison and the query becomes "createdAt" = '[object Object]'. That is a valid query against a value nothing equals, so there is nothing to report — you get zero rows and a success.

Fix. Use the query builder for every comparison:

const recent = await ctx.api.data.find('notes', {
where: { createdAt: q.gte(dayAgo) },
});

If a query is returning fewer rows than you expect, check every where clause for a bare object before you check anything else. See Typed data.

You narrowed an AI session to your extension’s tools, and the model behaves as if it has no tools at all.

// Matches nothing.
useAISession({ agent: 'meta', tools: { include: ['myapp.*'] } });

Cause. include matches an exact tool name or one trailing * — and a tool name can never contain a dot, because names are validated against ^[a-zA-Z0-9_-]+$ at registration. So a dotted prefix pattern matches zero tools, and an empty include list is not treated as “no filter”.

Fix. Namespace with underscores and match that:

useAISession({ agent: 'meta', tools: { include: ['myapp_*'] } });

Exact names work too: include: ['read_file', 'web_fetch'].

Calling a documented ai method does not compile

Section titled “Calling a documented ai method does not compile”

ai.chat, ai.chatWithUsage, and ai.completeWithUsage exist at runtime but are absent from the published type surface, so the call is a type error.

Fix. The typed surface is complete, stream, generateObject, and createSession. See AI sessions for what each is for, and the plugin API reference, which marks every member with whether it is in the published types.

ctx.api.db was removed. The surviving raw-SQL paths are the migrations[] array in your manifest and ctx.api.data.raw().

It returns null. On an app page the window’s own close button is the exit; there is no in-page back affordance to render. See Pages and windows.

The whole extension fails to activate after you add a tool

Section titled “The whole extension fails to activate after you add a tool”

Not one broken tool — the entire extension faults, and nothing it contributes appears.

Cause. Tool names must match ^[a-zA-Z0-9_-]+$. register throws on anything else, register runs inside activate(), and a throw there faults the extension rather than skipping the offending tool. A dot is the usual culprit, because dotted namespacing looks like the natural convention.

Fix. my_app_do_thing, not my_app.do_thing. See Contributing tools.

wamp init --install reports success and the host never loads anything

Section titled “wamp init --install reports success and the host never loads anything”

Cause. --install symlinks into ~/.wamp/extensions/dynamic/, but the host reads development extensions from <userData>/extensions/dynamic/ under the Electron user-data directory — and ~/.wamp has no extensions/ directory at all. Nothing is watching where the link was created.

Fix. Put the extension in the directory the host actually watches, or point the host at yours with the WAMP_DEV_EXTENSIONS environment variable, which is honored in packaged builds as well as in development. See The wamp CLI.

The CLI tells you to run a command that does not exist

Section titled “The CLI tells you to run a command that does not exist”

wamp init’s success message prints npx wamp install. There is no install command — the CLI implements only init — and @wamp/extension-sdk is not on the public npm registry, so npx wamp does not resolve either. Ignore the message; see The wamp CLI for what exists.

An engines.wamp mismatch does not stop anything

Section titled “An engines.wamp mismatch does not stop anything”

Cause. engines.wamp is not enforced at load. It is required for marketplace upload and displayed on the catalog listing, and that is all it does.

If activation is actually being refused on a version, the field responsible is compat.pluginApi. See the manifest reference.

contributes.tools in the manifest changes nothing

Section titled “contributes.tools in the manifest changes nothing”

The host manifest schema has no such key, and the schema strips unknown keys silently, so declaring tools there is a no-op. Runtime ctx.api.tools.register is the only path. The marketplace does count the field, so a catalog listing can advertise tools the installed extension does not register.

ctx.api.workspace.getOpenFiles() returns a hardcoded empty array and openFile() is a no-op, while both are in the published types — so the code compiles, runs, and has no effect. The plugin API reference marks every member of this kind.

A cron schedule is rejected, or never fires

Section titled “A cron schedule is rejected, or never fires”

Cause. The accepted forms are wider than standard cron in one direction and narrower in another. Six fields are accepted (a leading seconds field) as well as five, and a duration shorthand works: 30s, 5m, 1h, 1d. But there is no timezone parameter, and none of L, W, #, ?, or the @daily-style aliases are supported.

Fix. Use five or six numeric fields, or the duration shorthand. See Scheduled work.

Styling looks wrong, and overriding it does not help

Section titled “Styling looks wrong, and overriding it does not help”

Sizes and corners come out different from what the same Tailwind classes give you elsewhere, and /opacity modifiers appear to do nothing on some colors.

Cause. The @wamp/ui Tailwind preset replaces Tailwind’s own type and radius scales, so text-base is 13px and rounded-md is 8px. And /opacity modifiers are only defined for background, foreground, primary, and the semantic colors — on card, muted, or border they emit nothing at all.

Fix. Take sizes from the scale rather than assuming Tailwind’s defaults, and for a translucent surface use one of the tokens that supports it, or an inline style with the token. See Interface kit.

A permission you did not declare seems to work anyway

Section titled “A permission you did not declare seems to work anyway”

Most declarative permissions — ai, filesystem, network, terminal — are consent signals rather than runtime gates today; they are not withheld. The one that genuinely withholds is database, which gates cloud (the store whose records leave the machine); the local typed store is ungated.

Do not read this as permission to skip declarations. They are what a user sees when they install your extension, and the gates are expected to tighten. See Permissions.

Cause. The marketplace object key is taken from a version form field read while the upload streams, and the in-app publish does not send it. Any app whose manifest version is not 1.0.0 therefore gets a listing pointing at an object that was stored under a different key.

Fix. Until this is corrected, either publish at version 1.0.0, or use the HTTP upload path, which sends the field. Verify by actually downloading your own listing before you tell anyone about it. See Marketplace extension.

contributes/products[] scoping applies to one browse query, and only when the client sends ?product=. The detail, manifest, and download routes ignore it, and download needs no sign-in. It is curation, not access control — do not use it for anything you need kept private.