Appearance
Troubleshooting
Status: this page describes the designed sandboxed extension API, which does not run today. Only native extensions run. See what actually runs.
This guide covers common issues and solutions when developing BluePLM extensions.
Development Issues
Extension Not Loading
Symptoms:
- Extension doesn't appear in settings
- No activation messages in console
- Commands not available
Solutions:
Check manifest validity
bash# Validate manifest npx ajv validate -s schemas/extension-v1.schema.json -d extension.jsonVerify entry point exists
json// extension.json "main": "client/index.js" // This file must existCheck for syntax errors in code
bashnpm run typecheck npm run buildCheck activation events
json// At least one activation event required "activationEvents": ["onExtensionEnabled"]View logs
- Open DevTools:
Ctrl+Shift+I - Check Console for errors
- Check Network tab for failed requests
- Open DevTools:
Commands Not Registering
Symptoms:
- Command not in command palette
- Keybinding doesn't work
- "Command not found" error
Solutions:
Verify command is declared in manifest
json"contributes": { "commands": [ { "id": "myext.sync", // Must match registerCommand "title": "Sync Files" } ] }Check command registration in code
typescript// ID must match manifest api.commands.registerCommand('myext.sync', handler)Verify permissions
json"permissions": { "client": ["commands:register"] }Check
whenconditionjson{ "id": "myext.sync", "when": "vault.isOpen" // Command only available when vault is open }
Storage Not Persisting
Symptoms:
- Settings lost after restart
storage.getreturns undefined
Solutions:
Check permission
json"permissions": { "client": ["storage:local"] }Ensure value is JSON-serializable
typescript// ✓ Good: Plain objects await api.storage.set('config', { autoSync: true }) // ❌ Bad: Functions, Maps, Sets, etc. await api.storage.set('handler', myFunction) // Won't workVerify key names
typescriptawait api.storage.set('myKey', value) const result = await api.storage.get('myKey') // Same keyCheck for errors
typescripttry { await api.storage.set('key', value) } catch (error) { console.error('Storage error:', error) }
API Calls Failing
Symptoms:
- 401/403 errors from
callOrgApi - Network errors
- Timeout errors
Solutions:
Check authentication
typescript// Ensure user is logged in if (!api.context.user) { api.ui.showToast('Please log in first', 'error') return }Verify endpoint exists
json// Manifest must declare the route "contributes": { "apiRoutes": [ { "method": "POST", "path": "sync", // Endpoint: /extensions/myext/sync "handler": "server/sync.js" } ] }Check network permission
json"permissions": { "client": ["network:orgApi"] }Increase timeout for slow operations
typescriptconst response = await api.callOrgApi('/extensions/myext/sync', { timeout: 60000 // 60 seconds })Check server handler for errors
- View org API logs
- Add logging in handler
- Check for uncaught exceptions
Server Handler Issues
Handler Not Found
Symptoms:
- 404 error when calling endpoint
- "Route not found" error
Solutions:
Verify handler path
json"contributes": { "apiRoutes": [ { "method": "POST", "path": "sync", "handler": "server/sync.js" // Must match actual file } ] }Check handler exports
typescript// server/sync.ts export default async function handler(api) { // Must be default export }Ensure extension is installed on org
- Extension must be installed in organization
- Check org extension list
Domain Not Allowed
Symptoms:
- "Domain not in allowed domains" error
- HTTP requests fail
Solutions:
Declare domain in permissions
json"permissions": { "server": [ "http:domain:api.example.com", "http:domain:auth.example.com" ] }Use exact domain
typescript// Permission: http:domain:api.example.com await api.http.fetch('https://api.example.com/data') // ✓ Works await api.http.fetch('https://example.com/data') // ✗ Different domainCheck protocol
typescript// Always use HTTPS await api.http.fetch('https://api.example.com/data') // ✓ Good await api.http.fetch('http://api.example.com/data') // May fail
Secrets Not Working
Symptoms:
secrets.getreturns undefined- "Permission denied" errors
Solutions:
Check permissions
json"permissions": { "server": ["secrets:read", "secrets:write"] }Verify secret was stored
typescript// Must set before getting await api.secrets.set('api_key', 'value') const key = await api.secrets.get('api_key') // Now worksCheck secret limits
- Maximum 50 secrets per extension
- Maximum 10KB per secret
Build Issues
TypeScript Errors
Common errors and fixes:
typescript
// Error: Cannot find module '@blueplm/extension-api'
// Fix: Install types package or use local types
npm install @blueplm/extension-api --save-dev
// Or create local type file:
// types/extension-api.d.ts
declare module '@blueplm/extension-api' {
export interface ExtensionContext { ... }
export interface ExtensionClientAPI { ... }
}typescript
// Error: Type 'unknown' is not assignable to type 'X'
// Fix: Add type assertion or validation
const body = api.request.body as { vaultId: string }
// Better: Validate the type
function isValidBody(body: unknown): body is { vaultId: string } {
return typeof body === 'object' && body !== null && 'vaultId' in body
}Bundle Size Too Large
Symptoms:
- Package over 10MB limit
- Slow extension load times
Solutions:
Enable minification
bashesbuild ... --minifyExternalize React
bashesbuild ... --external:react --external:react-domTree shake unused code
typescript// ✓ Good: Named import import { specificFunction } from 'large-library' // ❌ Bad: Imports entire library import * as library from 'large-library'Check bundle analysis
bashesbuild ... --metafile=meta.json # Analyze meta.json to find large dependenciesLazy load components
typescriptconst HeavyComponent = React.lazy(() => import('./HeavyComponent'))
Runtime Issues
Memory Leaks
Symptoms:
- Extension slows down over time
- High memory usage
- Watchdog warnings
Solutions:
Clean up intervals/timeouts
typescriptlet intervalId: ReturnType<typeof setInterval> export function activate(context, api) { intervalId = setInterval(task, 5000) // Register cleanup context.subscriptions.push({ dispose: () => clearInterval(intervalId) }) }Remove event listeners
typescript// Use subscriptions for auto-cleanup context.subscriptions.push( api.workspace.onFileChanged(handler) // Auto-disposed )Clear caches
typescriptconst cache = new Map<string, Data>() // Limit cache size function addToCache(key: string, value: Data) { if (cache.size > 100) { const firstKey = cache.keys().next().value cache.delete(firstKey) } cache.set(key, value) }
Extension Crashes
Symptoms:
- Extension stops working
- Error messages in console
- Watchdog kills extension
Solutions:
Add error boundaries
typescriptexport async function activate(context, api) { try { await initialize(api) } catch (error) { context.log.error('Failed to activate:', error) api.ui.showToast('Extension failed to load', 'error') // Don't throw - allow graceful degradation } }Handle async errors
typescriptcontext.subscriptions.push( api.workspace.onFileChanged(async (events) => { try { await handleFileChanges(events) } catch (error) { context.log.error('File handler error:', error) // Don't crash on individual file errors } }) )Check memory limits
- Default: 50MB per extension
- Reduce memory usage or request increase
UI Issues
View Not Rendering
Symptoms:
- Blank panel
- Component errors
- "Cannot read property of undefined"
Solutions:
Check component path
json"contributes": { "views": [{ "component": "client/components/Panel.js" // Must match }] }Verify React component
tsx// Must be default export export default function Panel({ api }: { api: ExtensionClientAPI }) { return <div>Content</div> }Handle loading states
tsxfunction Panel({ api }) { const [loading, setLoading] = useState(true) const [data, setData] = useState(null) useEffect(() => { api.storage.get('data') .then(setData) .finally(() => setLoading(false)) }, [api]) if (loading) return <div>Loading...</div> if (!data) return <div>No data</div> return <div>{/* render data */}</div> }
Styling Issues
Symptoms:
- Styles not applied
- Theme conflicts
- Layout broken
Solutions:
Use Tailwind classes (provided by host)
tsx<div className="p-4 bg-white dark:bg-gray-800"> <h2 className="text-lg font-semibold">Title</h2> </div>Scope custom CSS
css/* Scope to your extension */ .myext-panel { /* your styles */ }Support dark mode
tsx<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
Getting Help
Debug Information to Collect
When reporting issues, include:
- Extension manifest (without secrets)
- Error messages from console
- BluePLM version (
Help → About) - OS version
- Steps to reproduce
Logging
Add comprehensive logging:
typescript
export async function activate(context, api) {
context.log.info('Extension starting...')
context.log.debug('Config:', await api.storage.get('config'))
try {
await initialize(api)
context.log.info('Extension ready')
} catch (error) {
context.log.error('Initialization failed:', error)
}
}Where to Get Help
- Documentation — You're here!
- GitHub Issues — bluerobotics/bluePLM
- Email — [email protected]
Quick Reference
| Issue | Common Cause | Quick Fix |
|---|---|---|
| Extension not loading | Invalid manifest | Validate with JSON Schema |
| Commands not working | Missing permission | Add commands:register |
| Storage not persisting | Non-serializable value | Use plain objects |
| API calls failing | Missing auth | Check api.context.user |
| Handler 404 | Wrong path | Match manifest route |
| Domain blocked | Not declared | Add http:domain:* |
| Memory leak | Missing cleanup | Use context.subscriptions |
| Bundle too large | Not minified | Enable minification |