Skip to content

Plugin marketplace HTTP 500 on install: nullable API fields crash non-nullable typed properties in MarketplacePlugin model (PHP 8.x) #3342

@shaunchokshi

Description

@shaunchokshi

What is your set up?

Self Hosted Server

Version

3.7.3

Describe the issue

What is your set up?
Self-hosted, non-Docker (bare metal / VPS with PHP-FPM). Also reproduced by other users on Docker (see linked report).

Version: 3.7.3

PHP Version: 8.4 (also affects 8.1+ due to strict typed property enforcement introduced in PHP 8.0)


Describe the issue

Opening the plugin marketplace or attempting to install any plugin throws an HTTP 500. The root cause is that MarketplacePlugin.php declares several properties as non-nullable typed (public string $excerpt, public int $vendorId, etc.), but the Leantime marketplace API returns null for one or more of these fields at runtime. PHP 8.0+ strictly enforces typed property assignments and throws a TypeError on null assignment, regardless of whether a default value is set.

The raw assignment in Build.php:131 ($property->$key = $value) performs no null coercion before writing API data to the model, so any null field from the API hits the type constraint and crashes the request.

This renders the entire plugin system unusable.

(also creating a PR with the fix that worked for me; only tested on Debian 13 VPS host - after applying the patch, the plugins seem to be installing fine)

Reproduction steps

  1. Install Leantime 3.7.3 on PHP 8.x (Docker or bare metal)
  2. Navigate to the plugin marketplace
  3. Attempt to view or install any plugin
  4. HTTP 500 is returned immediately
  5. Optionally: attempt to install a licensed plugin, input a purchased license key, click Install — same result

Error Logs (LEANTIMEFOLDER/storage/logs)

Two distinct errors, both from the same root cause:

Cannot assign null to property
Leantime\Domain\Plugins\Models\MarketplacePlugin::$excerpt of type string
at /var/www/leantime/app/Core/Support/Build.php:131
TypeError (code: 0):
Cannot assign null to property
Leantime\Domain\Plugins\Models\MarketplacePlugin::$excerpt of type string
#0 /var/www/leantime/app/Core/Support/Build.php(29): Build->setValue()
#1 /var/www/leantime/app/Domain/Plugins/Hxcontrollers/Details.php(41): Build->set()

Also reported on Docker by another user with identical stacktrace:

json_decode(): Passing null to parameter #1 ($json) of type string
... /app/Domain/Plugins/Hxcontrollers/Details.php:35

Cannot assign null to property ... excerpt of type string
... /app/Core/Support/Build.php:131

Root cause

app/Domain/Plugins/Models/MarketplacePlugin.php declares these as non-nullable typed properties:

public string $identifier = '';
public string $name = '';
public string $excerpt = '';       // ← crashes here first
public string $description = '';
public string $imageUrl = '';
public string $vendorDisplayName = '';
public int    $vendorId = 0;       // ← also at risk
public string $vendorEmail = '';
public string $marketplaceUrl = '';
public string $type = 'marketplace';
public string $marketplaceId = '';
public string $version = '';
public string $icon = '';

Note: some fields ($startingPrice, $calculatedMonthlyPrice, $license, $rating) are already correctly declared as ?string = null, indicating this was partially addressed previously but not applied consistently.

The setter in app/Core/Support/Build.php:131 performs a raw assignment with no null guard:

$property->$key = $value;  // no null coercion — crashes on typed non-nullable properties

Fix

Make all potentially-null API-sourced properties nullable. Minimal fix for MarketplacePlugin.php:

// Change all non-nullable string/int properties to nullable:
public ?string $identifier = '';
public ?string $name = '';
public ?string $excerpt = '';
public ?string $description = '';
public ?string $imageUrl = '';
public ?string $vendorDisplayName = '';
public ?int    $vendorId = 0;
public ?string $vendorEmail = '';
public ?string $marketplaceUrl = '';
public ?string $type = 'marketplace';
public ?string $marketplaceId = '';
public ?string $version = '';
public ?string $icon = '';

A more durable systemic fix would also add null coercion in Build.php::setValue() so that all models built from external API data are protected, e.g.:

// Before raw assignment, coerce null based on declared type
$property->$key = $value ?? match($declaredType) {
    'string' => '',
    'int'    => 0,
    'array'  => [],
    default  => null,
};

Additional context

This is deployment-agnostic — confirmed on both Docker Compose and bare-metal PHP-FPM installs. Any PHP 8.0+ deployment will hit this. PHP 7.x would have silently accepted the null assignment, which is why this may not have been caught before PHP 8.x became standard.


Metadata

Metadata

Assignees

Labels

Fixed and StagedFixed in Master and ready to be included in the next release

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions