coollabsio/coolify · error · Exception

Failed to update application settings

Error message

Failed to update application settings

What it means

Thrown by Application::setConfig() (app/Models/Application.php:2603) after the incoming JSON config passed both the 'required|json' check and the deep validator (config.build_pack, config.base_directory, config.publish_directory required strings, config.settings.is_static required boolean), but the subsequent persistence failed. The method splits 'settings' out of the config array, calls $this->update($config) and $this->settings()->update($settings) inside a try block, and any \Exception escaping those two Eloquent calls is swallowed and rethrown as this generic message. Because the original exception is discarded, the real cause (usually an Illuminate\Database\QueryException) is only visible in the Laravel log.

Source

Thrown at app/Models/Application.php:2603

        $deepValidator = Validator::make(['config' => $config], [
            'config.build_pack' => 'required|string',
            'config.base_directory' => 'required|string',
            'config.publish_directory' => 'required|string',
            'config.ports_exposes' => 'nullable|string',
            'config.settings.is_static' => 'required|boolean',
        ]);
        if ($deepValidator->fails()) {
            throw new \Exception('Invalid data');
        }
        $config = $deepValidator->validated()['config'];

        try {
            $settings = data_get($config, 'settings', []);
            data_forget($config, 'settings');
            $this->update($config);
            $this->settings()->update($settings);
        } catch (\Exception $e) {
            throw new \Exception('Failed to update application settings');
        }
    }
}

View on GitHub (pinned to 70b9acc424)

Solutions

  1. Check the Laravel log (storage/logs/laravel.log or the Nightwatch/Log viewer) for the underlying QueryException — the thrown message intentionally hides it, so the log is the only place the real error surfaces.
  2. Verify each value you pass fits the applications/application_settings columns (length limits, boolean true/false for settings.is_static — not the string "true", which fails earlier with 'Invalid data').
  3. Confirm the DB connection is healthy (php artisan db:monitor / tinker DB::select('select 1')) and retry the setConfig call.
  4. If you control the code, chain the previous exception so the cause is preserved: throw new \Exception('Failed to update application settings: '.$e->getMessage(), 0, $e).

Example fix

// before (app/Models/Application.php)
try {
    $settings = data_get($config, 'settings', []);
    data_forget($config, 'settings');
    $this->update($config);
    $this->settings()->update($settings);
} catch (\Exception $e) {
    throw new \Exception('Failed to update application settings');
}

// after — keep the cause instead of swallowing it
try {
    $settings = data_get($config, 'settings', []);
    data_forget($config, 'settings');
    $this->update($config);
    $this->settings()->update($settings);
} catch (\Exception $e) {
    report($e);
    throw new \Exception('Failed to update application settings: '.$e->getMessage(), 0, $e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate JSON payload shape and value lengths before calling setConfig
$decoded = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
    throw new \Exception('Invalid JSON format');
}
Validator::validate($decoded, [
    'build_pack' => ['required', 'string', 'max:255'],
    'base_directory' => ['required', 'string', 'max:255'],
    'publish_directory' => ['required', 'string', 'max:255'],
    'ports_exposes' => ['nullable', 'string', 'max:255'],
    'settings.is_static' => ['required', 'boolean'],
]);

Try / catch

try {
    $application->setConfig($json);
} catch (\Exception $e) {
    // Message is generic by design; the cause is only in the log.
    Log::error('setConfig failed', ['application' => $application->uuid, 'exception' => $e]);
    return back()->withErrors(['config' => 'Config rejected: verify field lengths and that the database is reachable.']);
}

Prevention

When it happens

Trigger: Calling $application->setConfig($json) where $json is valid JSON with all required keys, but the database rejects one of the writes: a value too long for its column (e.g. oversized base_directory/publish_directory string), a connection drop or lock timeout during UPDATE, an invalid value for a typed column (ports_exposes), or a settings row update that violates a constraint. Note the catch only catches \Exception, so a missing settings relation (\Error: call to a member function update() on null) bypasses this wrap entirely.

Common situations: Programmatically or via the advanced config editor saving a hand-edited coolify.json-style config with path strings longer than the column limit; saving while a migration is mid-flight; DB connection pool exhaustion during concurrent config saves; copying a config from one app to another with incompatible column lengths.

Related errors


AI-assisted analysis of coollabsio/coolify@70b9acc424 (2026-08-17). Data as JSON: /api/errors/7e240275804aa3bb. Report an issue: GitHub.