Appearance
Troubleshooting
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 |