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.
While writing code
Section titled “While writing code”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.
A session that should have tools has none
Section titled “A session that should have tools has none”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.
Raw SQLite calls fail
Section titled “Raw SQLite calls fail”ctx.api.db was removed. The surviving raw-SQL paths are the migrations[]
array in your manifest and ctx.api.data.raw().
AppShell.BackButton renders nothing
Section titled “AppShell.BackButton renders nothing”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.
While building and loading
Section titled “While building and loading”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.
A method you can call does nothing
Section titled “A method you can call does nothing”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.
While running
Section titled “While running”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.
After publishing
Section titled “After publishing”The listing appears but the download 404s
Section titled “The listing appears but the download 404s”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.
Product scoping is not hiding anything
Section titled “Product scoping is not hiding anything”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.