Files
inkreach-teable-cli/skills/inkreach-teable-plugin/dynamic-plugins/troubleshooting.md
T
yeuimu e09ed1bc0f feat: 收录 inkreach-teable-plugin skill;install.sh 安装全部技能
skills/ 现包含 inkreach-teable + inkreach-teable-plugin,
npx skills add 一并安装两个;install.sh 改为遍历安装 skills/*。
2026-09-23 06:23:46 +00:00

4.6 KiB

Common pitfalls and how to avoid them

These are real issues discovered during plugin development. Read before writing plugin logic — each one will save you debugging time.

Pitfall 1: Importing @teable/* packages instead of using host globals

// WRONG — webpack externals map these away at build time, so at runtime
// the import produces `undefined` or throws:
import { useFields } from '@teable/sdk';        // → undefined at runtime
import { Button } from '@teable/ui-lib';        // → undefined at runtime
import { definePlugin } from '@teable/plugin-sdk'; // fine (it re-exports from host)

// CORRECT — always read from the host global:
const host = window.__TEABLE_HOST__;
const { useFields } = host.SDK;
const { Button } = host.UILib;
const { definePlugin } = host.PluginSDK;
const React = host.React;

The webpack externals config in the scaffold template maps @teable/sdk → window.__TEABLE_HOST__.SDK at build time. This means the bundled code replaces the import with window.__TEABLE_HOST__.SDK, but destructuring from the import statement doesn't survive the webpack transformation — use direct host.* access instead. The only exception is @teable/plugin-sdk which re-exports from window.__TEABLE_HOST__.PluginSDK at the level webpack expects.

Symptoms: useFields is not a function, Cannot read properties of undefined, or components silently rendering nothing.

Pitfall 2: window.__TEABLE_HOST__ is undefined

The host object is injected by HostInitializer which mounts inside <AppProvider>. This happens after the initial React tree render. If you try to access window.__TEABLE_HOST__ in a script that executes before the host tree mounts, it will be undefined.

Fix: The PluginLoader component ensures the host is ready before loading plugin scripts, so plugins loaded through the normal flow don't hit this. But if you're testing a plugin directly (e.g. loading main.js in a browser console), you'll see __TEABLE_HOST__ is null until the app renders.

Pitfall 3: Upload returns 403 — not a developer

Only users in the plugin_developer DB table can upload bundles. The DeveloperGuard checks this. If you get 403 on upload-bundle, your user is not whitelisted.

Fix: Add the user to plugin_developer:

INSERT INTO plugin_developer (id, user_id, email, created_by)
VALUES (gen_random_uuid(), '<userId>', '<email>', '<adminUserId>');

Pitfall 4: Enable (plugin-config) must be called before install

Installing a plugin to a position (contextMenu / panel / view / dashboard) requires the plugin to be enabled first. If you skip enable and call install directly, you'll get 403.

The correct sequence is always:

  1. Upload bundle → get pluginId
  2. Enable via PUT /base/:baseId/plugin-config/:pluginId
  3. Install to position

Pitfall 5: getSelectionRecords returns empty array, not throw

The old iframe/ZIP bridge.getSelectionRecords() threw when nothing was selected. The dynamic system's PluginSDK.getSelectionRecords() returns an empty array instead. You still need to handle the empty case in your UI, but you don't need a try/catch wrapper.

const { getSelectionRecords } = host.PluginSDK;
const res = await getSelectionRecords(tableId, viewId, selection);
if (res.records.length === 0) {
  // Show "no records selected" message — no try/catch needed
}

Pitfall 6: Plugin component renders nothing / context menu empty

If your plugin uploads, enables, and installs successfully but the component never appears:

  1. Check plugin.json positions — it must include the position you're installing to (e.g. "positions": ["contextMenu"])
  2. Check onLoad registration — ctx.addContextMenuAction('main', Component) must be called inside onLoad
  3. Check Authority Matrix — the user's role must have the plugin enabled in AM. For the test account, the owner bypasses AM, but for other users, an admin must enable the plugin per role in the RoleEditor UI.
  4. Check browser console — errors in onLoad are silently caught; add a console.log at the top of onLoad to verify it runs.

Pitfall 7: Component uses JSX but webpack doesn't transpile it

The scaffold template uses ts-loader with jsx: "react-jsx" in tsconfig. If you're setting up the webpack config manually, make sure:

  • tsconfig.json has "jsx": "react-jsx" under compilerOptions
  • The plugin does NOT need import React from 'react' at the top when using react-jsx transform — JSX is auto-transpiled. But you still need const React = host.React for any explicit React.createElement calls.