Skip to content

Comments

fix: prevent plugins page crash when tags or features fields are missing#3398

Merged
MuhammadKhalilzadeh merged 2 commits intodevelopfrom
fix/plugins-page-missing-tags
Feb 23, 2026
Merged

fix: prevent plugins page crash when tags or features fields are missing#3398
MuhammadKhalilzadeh merged 2 commits intodevelopfrom
fix/plugins-page-missing-tags

Conversation

@gorkem-bwl
Copy link
Contributor

Summary

  • Plugins from the marketplace JSON (e.g. dataset-bulk-upload, model-lifecycle) may not include tags or features fields
  • The backend searchPlugins method called .some() directly on p.tags without a null-check, throwing TypeError when tags was undefined
  • This crashed the API response and caused the frontend plugins page to render blank

Changes

  • Add (p.tags || []) fallback in pluginService.ts search filter
  • Make tags and features optional in the frontend Plugin type to match actual marketplace data shape

Test plan

  • Navigate to the plugins page — verify it loads without blank screen
  • Search for a plugin by name — verify search works
  • Verify plugins without tags field (e.g. dataset-bulk-upload) appear correctly
  • Verify plugin detail page renders correctly when features is missing

Plugins from the marketplace JSON (e.g. dataset-bulk-upload, model-lifecycle)
may not include tags or features fields. The backend searchPlugins method
called .some() directly on p.tags without a null-check, causing it to throw
when tags was undefined and resulting in a blank plugins page.

## Changes
- Add (p.tags || []) fallback in pluginService.ts searchPlugins filter
- Make tags and features optional in the frontend Plugin type to match
  actual marketplace data shape
@MuhammadKhalilzadeh
Copy link
Collaborator

MuhammadKhalilzadeh commented Feb 23, 2026

CC: @gorkem-bwl

Bug Fix Report: Plugins Page Crash (Missing Tags/Features)

Branch: fix/plugins-page-missing-tags
Date: 2026-02-23


Problem

The Plugins page rendered a blank screen (white page crash) when running in Docker. The page worked fine in local development but consistently crashed in the production Docker environment.

Root Cause

The crash was caused by unguarded access to the features and tags array fields on plugin objects. Some plugins in the remote plugins.json marketplace manifest omit features and tags entirely (the fields are undefined), but the frontend code assumed they were always present arrays.

The specific crash site was in PluginCard/index.tsx:

// BEFORE (crashes when plugin.features is undefined)
{plugin.features.slice(0, 2).map((feature, index) => ( ... ))}

Calling .slice() on undefined throws a TypeError, which crashes the entire React render tree and produces a blank screen. A similar issue existed in the backend's searchPlugins method where p.tags.some(...) would throw if tags was missing.

The reason it worked locally but not in Docker: the local development environment likely had cached plugin data or a different plugins.json version where all plugins happened to include these fields.

Fixes Applied

1. Backend normalization (Servers/services/plugin/pluginService.ts)

Added default empty-array normalization in fetchRemoteMarketplace() so that all plugins returned from the marketplace API always have tags and features as arrays:

// In fetchRemoteMarketplace() - normalize when data arrives
data.plugins = data.plugins.map((plugin) => {
  if (plugin.iconUrl && !plugin.iconUrl.startsWith("http")) {
    plugin.iconUrl = `${PLUGIN_MARKETPLACE_BASE_URL}/${plugin.iconUrl}`;
  }
  plugin.tags = plugin.tags || [];       // added
  plugin.features = plugin.features || []; // added
  return plugin;
});

Also guarded the searchPlugins method:

// BEFORE
p.tags.some((tag) => tag.toLowerCase().includes(lowerQuery))
// AFTER
(p.tags || []).some((tag) => tag.toLowerCase().includes(lowerQuery))

2. Frontend PluginCard guard (Clients/src/presentation/components/PluginCard/index.tsx)

Added a fallback empty array directly at the render site to prevent the crash even if upstream normalization is bypassed:

// BEFORE
{plugin.features.slice(0, 2).map((feature, index) => ( ... ))}

// AFTER
{(plugin.features || []).slice(0, 2).map((feature, index) => ( ... ))}

3. Frontend hook normalization (Clients/src/application/hooks/usePlugins.ts)

Added normalization in the usePlugins hook when merging marketplace and installation data, ensuring every plugin object flowing into React state has proper arrays:

const mergedPlugins = marketplacePlugins.map((plugin) => {
  const normalized = {
    ...plugin,
    tags: plugin.tags || [],
    features: plugin.features || [],
  };
  // ... rest of merge logic uses `normalized` instead of `plugin`
});

4. Type definition update (Clients/src/domain/types/plugins.ts)

Made features and tags optional in the Plugin interface to accurately reflect the API contract:

// BEFORE
features: PluginFeature[];
tags: string[];

// AFTER
features?: PluginFeature[];
tags?: string[];

Defense in Depth

The fix applies normalization at three layers:

Layer File Purpose
Backend API pluginService.ts Normalize data at the source
Frontend hook usePlugins.ts Normalize before state storage
Frontend component PluginCard/index.tsx Guard at render site

This ensures the bug cannot recur even if one layer is bypassed (e.g., a different API consumer, a code path that skips the hook, or a direct prop pass).

Verification

Both TypeScript compilation checks pass cleanly:

  • Backend: cd Servers && npx tsc --noEmit - no errors
  • Frontend: cd Clients && npx tsc --noEmit - no errors

@MuhammadKhalilzadeh
Copy link
Collaborator

@gorkem-bwl
Following build errors fixed in this same PR as well:

image

@MuhammadKhalilzadeh MuhammadKhalilzadeh merged commit e90a556 into develop Feb 23, 2026
5 checks passed
@gorkem-bwl gorkem-bwl deleted the fix/plugins-page-missing-tags branch February 23, 2026 16:07
@gorkem-bwl
Copy link
Contributor Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants