Browse API 1 documentation
Troubleshooting
Diagnose manifest, permission, bridge, network, and runtime failures.
Start with the extension’s Console in Grove Manager. It combines console output, grove.log messages, uncaught JavaScript errors, blocked navigation and sandbox failures, and WebContent termination notices.
When asking for help, include the Mango version, manifest.json, the relevant bridge call, and the smallest safe Console excerpt. Remove tokens, message text, local paths, and other private data.
The package will not load
Check these causes in order:
manifest.jsonis missing from the selected directory;groveis not the integer1;idhas no dot or starts or ends with a dot;- a capability string is misspelled or undocumented;
entry,settings,icon,readme, or a screenshot does not exist;- a path is absolute, escapes with
.., resolves through a symlink outside the package, or names a directory; - the README is not Markdown or a screenshot is not PNG, JPEG, or WebP;
- an upload contribution is not a headless Root, lacks
uploads.provide, duplicates an identifier, or has an invalid limit.
For a built project, inspect the final output directory or .grove archive rather than the source tree. Vite and similar tools may copy manifest.json but omit files that are not part of their public/static asset pipeline.
An event never arrives
Verify all three parts of the route:
- the manifest requests the documented capability;
- the user accepted that capability for the installed package;
- the running instance is bound to the channel, query, or server scope where the event occurs.
Application-scoped Roots intentionally receive no IRC event stream.
Ordinary message delivery is live-only. A Root that starts after bouncer history arrives should not expect those historical rows as new message events.
Reserved capability names such as server.events and identity do not have API 1 events or getters. channel.members and channel.metadata are implemented through the retained channelSnapshots event.
A bridge call does nothing
Most Grove methods are fire-and-forget; a native rejection appears in the Console rather than as a JavaScript exception. Confirm:
- the matching capability is granted;
- the call is valid for the surface and scope;
- a targeted send uses the correct query permission;
- a server-scoped
{ channel }send hasnetwork.send, names one strict channel, and the network has a confirmed matching join; - a registered identifier was declared and has a valid shape;
- a composer method is called only by an exclusive composer utility;
- the Root has not stopped or lost its grant.
Built-in slash commands cannot be overridden. Command collisions between extensions are first-wins.
Fetch or WebSocket is blocked
The exact scheme and host must appear in hostPermissions. https://api.example.com does not grant wss://api.example.com, a sibling subdomain, or cleartext access. The only narrow wildcard is a complete leftmost label such as https://*.example.com; it matches one direct subdomain, not the apex or a deeper name. Other partial wildcards are not supported.
A settings page always has an empty network allow-list. Save configuration there, then let the running extension perform the request.
Navigation, window.open, frames, forms, WebRTC, and undeclared beacons are intentionally blocked and are not supported network strategies.
Persistent state disappears or leaks across scopes
Request and obtain storage for persistent Web storage. Without it, local storage is ephemeral.
API 1 shares the WebKit store between instances of the same extension identifier. Include grove.info.networkID, scope kind, and target in keys when data must be per-network or per-buffer.
Use storage.secure and grove.secrets for credentials. The default secret namespace is per network; grove.secrets.application is shared across network instances.
An upload provider is unavailable
Confirm that:
- the installed manifest declares an upload provider;
- the extension is an exclusive headless Root with
scope: "application"for one destination shared across networks, orscope: "server"for deliberately per-network configuration; uploads.provideis granted;- the Root is running for that IRC session, or the application-scoped Root has been started and mirrored into it;
- JavaScript called
grove.uploads.registerwith the exact declared identifier; - the selected file matches image and size restrictions;
- endpoint configuration is present and its origin is allowed.
File reads must be positive, inside the file, and no larger than 1 MiB. Return an http or https URL. A TUS upload location is not necessarily a recipient-accessible download URL.
A filter behaves unexpectedly
Filters are ordered and may see changes made by an earlier filter. Use wireText for what IRC will receive and displayText for what Mango will show. Avoid the legacy string replacement form when those values may differ.
A timeout, thrown error, WebContent crash, or invalid result fails open and allows the message. Only a valid explicit deny drops it. Inspect the Console and keep filter work bounded so it can complete before the native timeout.
The extension stops in the background
Grove Roots are client-side WebKit runtimes, not server workers. Listen for lifecycle events, reconnect sockets after foreground or pulse, persist necessary state, and accept that iOS suspension stops reliable execution. Move truly always-on work to a backend and keep the Grove extension as its capability-scoped client.