diff --git a/v2/examples/panic-recovery-test/README.md b/v2/examples/panic-recovery-test/README.md
new file mode 100644
index 000000000..c0a6a7e5a
--- /dev/null
+++ b/v2/examples/panic-recovery-test/README.md
@@ -0,0 +1,76 @@
+# Panic Recovery Test
+
+This example demonstrates the Linux signal handler issue (#3965) and verifies the fix using `runtime.ResetSignalHandlers()`.
+
+## The Problem
+
+On Linux, WebKit installs signal handlers without the `SA_ONSTACK` flag, which prevents Go from recovering panics caused by nil pointer dereferences (SIGSEGV). Without the fix, the application crashes with:
+
+```
+signal 11 received but handler not on signal stack
+fatal error: non-Go code set up signal handler without SA_ONSTACK flag
+```
+
+## The Solution
+
+Call `runtime.ResetSignalHandlers()` immediately before code that might panic:
+
+```go
+import "github.com/wailsapp/wails/v2/pkg/runtime"
+
+go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("Recovered: %v", err)
+ }
+ }()
+ runtime.ResetSignalHandlers()
+ // Code that might panic...
+}()
+```
+
+## How to Reproduce
+
+### Prerequisites
+
+- Linux with WebKit2GTK 4.1 installed
+- Go 1.21+
+- Wails CLI
+
+### Steps
+
+1. Build the example:
+ ```bash
+ cd v2/examples/panic-recovery-test
+ wails build -tags webkit2_41
+ ```
+
+2. Run the application:
+ ```bash
+ ./build/bin/panic-recovery-test
+ ```
+
+3. Wait ~10 seconds (the app auto-calls `Greet` after 5s, then waits another 5s before the nil pointer dereference)
+
+### Expected Result (with fix)
+
+The panic is recovered and you see:
+```
+------------------------------"invalid memory address or nil pointer dereference"
+```
+
+The application continues running.
+
+### Without the fix
+
+Comment out the `runtime.ResetSignalHandlers()` call in `app.go` and rebuild. The application will crash with a fatal signal 11 error.
+
+## Files
+
+- `app.go` - Contains the `Greet` function that demonstrates panic recovery
+- `frontend/src/main.js` - Auto-calls `Greet` after 5 seconds to trigger the test
+
+## Related
+
+- Issue: https://github.com/wailsapp/wails/issues/3965
+- Original fix PR: https://github.com/wailsapp/wails/pull/2152
diff --git a/v2/examples/panic-recovery-test/app.go b/v2/examples/panic-recovery-test/app.go
new file mode 100644
index 000000000..ceb46e8d5
--- /dev/null
+++ b/v2/examples/panic-recovery-test/app.go
@@ -0,0 +1,44 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+)
+
+// App struct
+type App struct {
+ ctx context.Context
+}
+
+// NewApp creates a new App application struct
+func NewApp() *App {
+ return &App{}
+}
+
+// startup is called when the app starts. The context is saved
+// so we can call the runtime methods
+func (a *App) startup(ctx context.Context) {
+ a.ctx = ctx
+}
+
+// Greet returns a greeting for the given name
+func (a *App) Greet(name string) string {
+ go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ fmt.Printf("------------------------------%#v\n", err)
+ }
+ }()
+ time.Sleep(5 * time.Second)
+ // Fix signal handlers right before potential panic using the Wails runtime
+ runtime.ResetSignalHandlers()
+ // Nil pointer dereference - causes SIGSEGV
+ var t *time.Time
+ fmt.Println(t.Unix())
+ }()
+
+ return fmt.Sprintf("Hello %s, It's show time!", name)
+}
diff --git a/v2/examples/panic-recovery-test/frontend/index.html b/v2/examples/panic-recovery-test/frontend/index.html
new file mode 100644
index 000000000..d7aa4e942
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ panic-test
+
+
+
+
+
+
diff --git a/v2/examples/panic-recovery-test/frontend/package.json b/v2/examples/panic-recovery-test/frontend/package.json
new file mode 100644
index 000000000..a1b6f8e1a
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "frontend",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "vite": "^3.0.7"
+ }
+}
\ No newline at end of file
diff --git a/v2/examples/panic-recovery-test/frontend/src/app.css b/v2/examples/panic-recovery-test/frontend/src/app.css
new file mode 100644
index 000000000..59d06f692
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/src/app.css
@@ -0,0 +1,54 @@
+#logo {
+ display: block;
+ width: 50%;
+ height: 50%;
+ margin: auto;
+ padding: 10% 0 0;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
+ background-origin: content-box;
+}
+
+.result {
+ height: 20px;
+ line-height: 20px;
+ margin: 1.5rem auto;
+}
+
+.input-box .btn {
+ width: 60px;
+ height: 30px;
+ line-height: 30px;
+ border-radius: 3px;
+ border: none;
+ margin: 0 0 0 20px;
+ padding: 0 8px;
+ cursor: pointer;
+}
+
+.input-box .btn:hover {
+ background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);
+ color: #333333;
+}
+
+.input-box .input {
+ border: none;
+ border-radius: 3px;
+ outline: none;
+ height: 30px;
+ line-height: 30px;
+ padding: 0 10px;
+ background-color: rgba(240, 240, 240, 1);
+ -webkit-font-smoothing: antialiased;
+}
+
+.input-box .input:hover {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
+
+.input-box .input:focus {
+ border: none;
+ background-color: rgba(255, 255, 255, 1);
+}
\ No newline at end of file
diff --git a/v2/examples/panic-recovery-test/frontend/src/assets/fonts/OFL.txt b/v2/examples/panic-recovery-test/frontend/src/assets/fonts/OFL.txt
new file mode 100644
index 000000000..9cac04ce8
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/src/assets/fonts/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com),
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/v2/examples/panic-recovery-test/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 b/v2/examples/panic-recovery-test/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2
new file mode 100644
index 000000000..2f9cc5964
Binary files /dev/null and b/v2/examples/panic-recovery-test/frontend/src/assets/fonts/nunito-v16-latin-regular.woff2 differ
diff --git a/v2/examples/panic-recovery-test/frontend/src/assets/images/logo-universal.png b/v2/examples/panic-recovery-test/frontend/src/assets/images/logo-universal.png
new file mode 100644
index 000000000..d63303bfa
Binary files /dev/null and b/v2/examples/panic-recovery-test/frontend/src/assets/images/logo-universal.png differ
diff --git a/v2/examples/panic-recovery-test/frontend/src/main.js b/v2/examples/panic-recovery-test/frontend/src/main.js
new file mode 100644
index 000000000..ea5e74fc6
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/src/main.js
@@ -0,0 +1,55 @@
+import './style.css';
+import './app.css';
+
+import logo from './assets/images/logo-universal.png';
+import {Greet} from '../wailsjs/go/main/App';
+
+document.querySelector('#app').innerHTML = `
+
+
Please enter your name below 👇
+
+
+
+
+
+`;
+document.getElementById('logo').src = logo;
+
+let nameElement = document.getElementById("name");
+nameElement.focus();
+let resultElement = document.getElementById("result");
+
+// Setup the greet function
+window.greet = function () {
+ // Get name
+ let name = nameElement.value;
+
+ // Check if the input is empty
+ if (name === "") return;
+
+ // Call App.Greet(name)
+ try {
+ Greet(name)
+ .then((result) => {
+ // Update result with data back from App.Greet()
+ resultElement.innerText = result;
+ })
+ .catch((err) => {
+ console.error(err);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+};
+
+// Auto-call Greet after 5 seconds to trigger the panic test
+setTimeout(() => {
+ console.log("Auto-calling Greet to trigger panic test...");
+ Greet("PanicTest")
+ .then((result) => {
+ resultElement.innerText = result + " (auto-called - panic will occur in 5s)";
+ })
+ .catch((err) => {
+ console.error("Error:", err);
+ });
+}, 5000);
diff --git a/v2/examples/panic-recovery-test/frontend/src/style.css b/v2/examples/panic-recovery-test/frontend/src/style.css
new file mode 100644
index 000000000..3940d6c63
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/src/style.css
@@ -0,0 +1,26 @@
+html {
+ background-color: rgba(27, 38, 54, 1);
+ text-align: center;
+ color: white;
+}
+
+body {
+ margin: 0;
+ color: white;
+ font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
+ "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+ sans-serif;
+}
+
+@font-face {
+ font-family: "Nunito";
+ font-style: normal;
+ font-weight: 400;
+ src: local(""),
+ url("assets/fonts/nunito-v16-latin-regular.woff2") format("woff2");
+}
+
+#app {
+ height: 100vh;
+ text-align: center;
+}
diff --git a/v2/examples/panic-recovery-test/frontend/wailsjs/go/main/App.d.ts b/v2/examples/panic-recovery-test/frontend/wailsjs/go/main/App.d.ts
new file mode 100755
index 000000000..02a3bb988
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/wailsjs/go/main/App.d.ts
@@ -0,0 +1,4 @@
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function Greet(arg1:string):Promise;
diff --git a/v2/examples/panic-recovery-test/frontend/wailsjs/go/main/App.js b/v2/examples/panic-recovery-test/frontend/wailsjs/go/main/App.js
new file mode 100755
index 000000000..c71ae77cb
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/wailsjs/go/main/App.js
@@ -0,0 +1,7 @@
+// @ts-check
+// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
+// This file is automatically generated. DO NOT EDIT
+
+export function Greet(arg1) {
+ return window['go']['main']['App']['Greet'](arg1);
+}
diff --git a/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/package.json b/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/package.json
new file mode 100644
index 000000000..1e7c8a5d7
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@wailsapp/runtime",
+ "version": "2.0.0",
+ "description": "Wails Javascript runtime library",
+ "main": "runtime.js",
+ "types": "runtime.d.ts",
+ "scripts": {
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/wailsapp/wails.git"
+ },
+ "keywords": [
+ "Wails",
+ "Javascript",
+ "Go"
+ ],
+ "author": "Lea Anthony ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/wailsapp/wails/issues"
+ },
+ "homepage": "https://github.com/wailsapp/wails#readme"
+}
diff --git a/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/runtime.d.ts b/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/runtime.d.ts
new file mode 100644
index 000000000..4445dac21
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/runtime.d.ts
@@ -0,0 +1,249 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The electron alternative for Go
+(c) Lea Anthony 2019-present
+*/
+
+export interface Position {
+ x: number;
+ y: number;
+}
+
+export interface Size {
+ w: number;
+ h: number;
+}
+
+export interface Screen {
+ isCurrent: boolean;
+ isPrimary: boolean;
+ width : number
+ height : number
+}
+
+// Environment information such as platform, buildtype, ...
+export interface EnvironmentInfo {
+ buildType: string;
+ platform: string;
+ arch: string;
+}
+
+// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
+// emits the given event. Optional data may be passed with the event.
+// This will trigger any event listeners.
+export function EventsEmit(eventName: string, ...data: any): void;
+
+// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
+export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
+
+// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
+// sets up a listener for the given event name, but will only trigger a given number times.
+export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
+
+// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
+// sets up a listener for the given event name, but will only trigger once.
+export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
+
+// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
+// unregisters the listener for the given event name.
+export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
+
+// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
+// unregisters all listeners.
+export function EventsOffAll(): void;
+
+// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
+// logs the given message as a raw message
+export function LogPrint(message: string): void;
+
+// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
+// logs the given message at the `trace` log level.
+export function LogTrace(message: string): void;
+
+// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
+// logs the given message at the `debug` log level.
+export function LogDebug(message: string): void;
+
+// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
+// logs the given message at the `error` log level.
+export function LogError(message: string): void;
+
+// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
+// logs the given message at the `fatal` log level.
+// The application will quit after calling this method.
+export function LogFatal(message: string): void;
+
+// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
+// logs the given message at the `info` log level.
+export function LogInfo(message: string): void;
+
+// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
+// logs the given message at the `warning` log level.
+export function LogWarning(message: string): void;
+
+// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
+// Forces a reload by the main application as well as connected browsers.
+export function WindowReload(): void;
+
+// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
+// Reloads the application frontend.
+export function WindowReloadApp(): void;
+
+// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
+// Sets the window AlwaysOnTop or not on top.
+export function WindowSetAlwaysOnTop(b: boolean): void;
+
+// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
+// *Windows only*
+// Sets window theme to system default (dark/light).
+export function WindowSetSystemDefaultTheme(): void;
+
+// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
+// *Windows only*
+// Sets window to light theme.
+export function WindowSetLightTheme(): void;
+
+// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
+// *Windows only*
+// Sets window to dark theme.
+export function WindowSetDarkTheme(): void;
+
+// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
+// Centers the window on the monitor the window is currently on.
+export function WindowCenter(): void;
+
+// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
+// Sets the text in the window title bar.
+export function WindowSetTitle(title: string): void;
+
+// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
+// Makes the window full screen.
+export function WindowFullscreen(): void;
+
+// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
+// Restores the previous window dimensions and position prior to full screen.
+export function WindowUnfullscreen(): void;
+
+// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
+// Returns the state of the window, i.e. whether the window is in full screen mode or not.
+export function WindowIsFullscreen(): Promise;
+
+// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
+// Sets the width and height of the window.
+export function WindowSetSize(width: number, height: number): void;
+
+// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
+// Gets the width and height of the window.
+export function WindowGetSize(): Promise;
+
+// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
+// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
+// Setting a size of 0,0 will disable this constraint.
+export function WindowSetMaxSize(width: number, height: number): void;
+
+// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
+// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
+// Setting a size of 0,0 will disable this constraint.
+export function WindowSetMinSize(width: number, height: number): void;
+
+// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
+// Sets the window position relative to the monitor the window is currently on.
+export function WindowSetPosition(x: number, y: number): void;
+
+// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
+// Gets the window position relative to the monitor the window is currently on.
+export function WindowGetPosition(): Promise;
+
+// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
+// Hides the window.
+export function WindowHide(): void;
+
+// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
+// Shows the window, if it is currently hidden.
+export function WindowShow(): void;
+
+// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
+// Maximises the window to fill the screen.
+export function WindowMaximise(): void;
+
+// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
+// Toggles between Maximised and UnMaximised.
+export function WindowToggleMaximise(): void;
+
+// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
+// Restores the window to the dimensions and position prior to maximising.
+export function WindowUnmaximise(): void;
+
+// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
+// Returns the state of the window, i.e. whether the window is maximised or not.
+export function WindowIsMaximised(): Promise;
+
+// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
+// Minimises the window.
+export function WindowMinimise(): void;
+
+// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
+// Restores the window to the dimensions and position prior to minimising.
+export function WindowUnminimise(): void;
+
+// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
+// Returns the state of the window, i.e. whether the window is minimised or not.
+export function WindowIsMinimised(): Promise;
+
+// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
+// Returns the state of the window, i.e. whether the window is normal or not.
+export function WindowIsNormal(): Promise;
+
+// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
+// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
+export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
+
+// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
+// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
+export function ScreenGetAll(): Promise;
+
+// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
+// Opens the given URL in the system browser.
+export function BrowserOpenURL(url: string): void;
+
+// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
+// Returns information about the environment
+export function Environment(): Promise;
+
+// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
+// Quits the application.
+export function Quit(): void;
+
+// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
+// Hides the application.
+export function Hide(): void;
+
+// [Show](https://wails.io/docs/reference/runtime/intro#show)
+// Shows the application.
+export function Show(): void;
+
+// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
+// Returns the current text stored on clipboard
+export function ClipboardGetText(): Promise;
+
+// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
+// Sets a text on the clipboard
+export function ClipboardSetText(text: string): Promise;
+
+// [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop)
+// OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
+export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void
+
+// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff)
+// OnFileDropOff removes the drag and drop listeners and handlers.
+export function OnFileDropOff() :void
+
+// Check if the file path resolver is available
+export function CanResolveFilePaths(): boolean;
+
+// Resolves file paths for an array of files
+export function ResolveFilePaths(files: File[]): void
\ No newline at end of file
diff --git a/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/runtime.js b/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/runtime.js
new file mode 100644
index 000000000..7cb89d750
--- /dev/null
+++ b/v2/examples/panic-recovery-test/frontend/wailsjs/runtime/runtime.js
@@ -0,0 +1,242 @@
+/*
+ _ __ _ __
+| | / /___ _(_) /____
+| | /| / / __ `/ / / ___/
+| |/ |/ / /_/ / / (__ )
+|__/|__/\__,_/_/_/____/
+The electron alternative for Go
+(c) Lea Anthony 2019-present
+*/
+
+export function LogPrint(message) {
+ window.runtime.LogPrint(message);
+}
+
+export function LogTrace(message) {
+ window.runtime.LogTrace(message);
+}
+
+export function LogDebug(message) {
+ window.runtime.LogDebug(message);
+}
+
+export function LogInfo(message) {
+ window.runtime.LogInfo(message);
+}
+
+export function LogWarning(message) {
+ window.runtime.LogWarning(message);
+}
+
+export function LogError(message) {
+ window.runtime.LogError(message);
+}
+
+export function LogFatal(message) {
+ window.runtime.LogFatal(message);
+}
+
+export function EventsOnMultiple(eventName, callback, maxCallbacks) {
+ return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
+}
+
+export function EventsOn(eventName, callback) {
+ return EventsOnMultiple(eventName, callback, -1);
+}
+
+export function EventsOff(eventName, ...additionalEventNames) {
+ return window.runtime.EventsOff(eventName, ...additionalEventNames);
+}
+
+export function EventsOffAll() {
+ return window.runtime.EventsOffAll();
+}
+
+export function EventsOnce(eventName, callback) {
+ return EventsOnMultiple(eventName, callback, 1);
+}
+
+export function EventsEmit(eventName) {
+ let args = [eventName].slice.call(arguments);
+ return window.runtime.EventsEmit.apply(null, args);
+}
+
+export function WindowReload() {
+ window.runtime.WindowReload();
+}
+
+export function WindowReloadApp() {
+ window.runtime.WindowReloadApp();
+}
+
+export function WindowSetAlwaysOnTop(b) {
+ window.runtime.WindowSetAlwaysOnTop(b);
+}
+
+export function WindowSetSystemDefaultTheme() {
+ window.runtime.WindowSetSystemDefaultTheme();
+}
+
+export function WindowSetLightTheme() {
+ window.runtime.WindowSetLightTheme();
+}
+
+export function WindowSetDarkTheme() {
+ window.runtime.WindowSetDarkTheme();
+}
+
+export function WindowCenter() {
+ window.runtime.WindowCenter();
+}
+
+export function WindowSetTitle(title) {
+ window.runtime.WindowSetTitle(title);
+}
+
+export function WindowFullscreen() {
+ window.runtime.WindowFullscreen();
+}
+
+export function WindowUnfullscreen() {
+ window.runtime.WindowUnfullscreen();
+}
+
+export function WindowIsFullscreen() {
+ return window.runtime.WindowIsFullscreen();
+}
+
+export function WindowGetSize() {
+ return window.runtime.WindowGetSize();
+}
+
+export function WindowSetSize(width, height) {
+ window.runtime.WindowSetSize(width, height);
+}
+
+export function WindowSetMaxSize(width, height) {
+ window.runtime.WindowSetMaxSize(width, height);
+}
+
+export function WindowSetMinSize(width, height) {
+ window.runtime.WindowSetMinSize(width, height);
+}
+
+export function WindowSetPosition(x, y) {
+ window.runtime.WindowSetPosition(x, y);
+}
+
+export function WindowGetPosition() {
+ return window.runtime.WindowGetPosition();
+}
+
+export function WindowHide() {
+ window.runtime.WindowHide();
+}
+
+export function WindowShow() {
+ window.runtime.WindowShow();
+}
+
+export function WindowMaximise() {
+ window.runtime.WindowMaximise();
+}
+
+export function WindowToggleMaximise() {
+ window.runtime.WindowToggleMaximise();
+}
+
+export function WindowUnmaximise() {
+ window.runtime.WindowUnmaximise();
+}
+
+export function WindowIsMaximised() {
+ return window.runtime.WindowIsMaximised();
+}
+
+export function WindowMinimise() {
+ window.runtime.WindowMinimise();
+}
+
+export function WindowUnminimise() {
+ window.runtime.WindowUnminimise();
+}
+
+export function WindowSetBackgroundColour(R, G, B, A) {
+ window.runtime.WindowSetBackgroundColour(R, G, B, A);
+}
+
+export function ScreenGetAll() {
+ return window.runtime.ScreenGetAll();
+}
+
+export function WindowIsMinimised() {
+ return window.runtime.WindowIsMinimised();
+}
+
+export function WindowIsNormal() {
+ return window.runtime.WindowIsNormal();
+}
+
+export function BrowserOpenURL(url) {
+ window.runtime.BrowserOpenURL(url);
+}
+
+export function Environment() {
+ return window.runtime.Environment();
+}
+
+export function Quit() {
+ window.runtime.Quit();
+}
+
+export function Hide() {
+ window.runtime.Hide();
+}
+
+export function Show() {
+ window.runtime.Show();
+}
+
+export function ClipboardGetText() {
+ return window.runtime.ClipboardGetText();
+}
+
+export function ClipboardSetText(text) {
+ return window.runtime.ClipboardSetText(text);
+}
+
+/**
+ * Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
+ *
+ * @export
+ * @callback OnFileDropCallback
+ * @param {number} x - x coordinate of the drop
+ * @param {number} y - y coordinate of the drop
+ * @param {string[]} paths - A list of file paths.
+ */
+
+/**
+ * OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings.
+ *
+ * @export
+ * @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished.
+ * @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target)
+ */
+export function OnFileDrop(callback, useDropTarget) {
+ return window.runtime.OnFileDrop(callback, useDropTarget);
+}
+
+/**
+ * OnFileDropOff removes the drag and drop listeners and handlers.
+ */
+export function OnFileDropOff() {
+ return window.runtime.OnFileDropOff();
+}
+
+export function CanResolveFilePaths() {
+ return window.runtime.CanResolveFilePaths();
+}
+
+export function ResolveFilePaths(files) {
+ return window.runtime.ResolveFilePaths(files);
+}
\ No newline at end of file
diff --git a/v2/examples/panic-recovery-test/go.mod b/v2/examples/panic-recovery-test/go.mod
new file mode 100644
index 000000000..026042cbf
--- /dev/null
+++ b/v2/examples/panic-recovery-test/go.mod
@@ -0,0 +1,5 @@
+module panic-recovery-test
+
+go 1.21
+
+require github.com/wailsapp/wails/v2 v2.11.0
diff --git a/v2/examples/panic-recovery-test/main.go b/v2/examples/panic-recovery-test/main.go
new file mode 100644
index 000000000..f6a38e86c
--- /dev/null
+++ b/v2/examples/panic-recovery-test/main.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "embed"
+
+ "github.com/wailsapp/wails/v2"
+ "github.com/wailsapp/wails/v2/pkg/options"
+ "github.com/wailsapp/wails/v2/pkg/options/assetserver"
+)
+
+//go:embed all:frontend/dist
+var assets embed.FS
+
+func main() {
+ // Create an instance of the app structure
+ app := NewApp()
+
+ // Create application with options
+ err := wails.Run(&options.App{
+ Title: "panic-test",
+ Width: 1024,
+ Height: 768,
+ AssetServer: &assetserver.Options{
+ Assets: assets,
+ },
+ BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
+ OnStartup: app.startup,
+ Bind: []interface{}{
+ app,
+ },
+ })
+
+ if err != nil {
+ println("Error:", err.Error())
+ }
+}
diff --git a/v2/examples/panic-recovery-test/wails.json b/v2/examples/panic-recovery-test/wails.json
new file mode 100644
index 000000000..56770f091
--- /dev/null
+++ b/v2/examples/panic-recovery-test/wails.json
@@ -0,0 +1,13 @@
+{
+ "$schema": "https://wails.io/schemas/config.v2.json",
+ "name": "panic-recovery-test",
+ "outputfilename": "panic-recovery-test",
+ "frontend:install": "npm install",
+ "frontend:build": "npm run build",
+ "frontend:dev:watcher": "npm run dev",
+ "frontend:dev:serverUrl": "auto",
+ "author": {
+ "name": "Lea Anthony",
+ "email": "lea.anthony@gmail.com"
+ }
+}
diff --git a/v2/pkg/runtime/signal_linux.go b/v2/pkg/runtime/signal_linux.go
new file mode 100644
index 000000000..6a7ed5db3
--- /dev/null
+++ b/v2/pkg/runtime/signal_linux.go
@@ -0,0 +1,65 @@
+//go:build linux
+
+package runtime
+
+/*
+#include
+#include
+#include
+#include
+
+static void fix_signal(int signum)
+{
+ struct sigaction st;
+
+ if (sigaction(signum, NULL, &st) < 0) {
+ return;
+ }
+ st.sa_flags |= SA_ONSTACK;
+ sigaction(signum, &st, NULL);
+}
+
+static void fix_all_signals()
+{
+#if defined(SIGSEGV)
+ fix_signal(SIGSEGV);
+#endif
+#if defined(SIGBUS)
+ fix_signal(SIGBUS);
+#endif
+#if defined(SIGFPE)
+ fix_signal(SIGFPE);
+#endif
+#if defined(SIGABRT)
+ fix_signal(SIGABRT);
+#endif
+}
+*/
+import "C"
+
+// ResetSignalHandlers resets signal handlers to allow panic recovery.
+//
+// On Linux, WebKit (used for the webview) may install signal handlers without
+// the SA_ONSTACK flag, which prevents Go from properly recovering from panics
+// caused by nil pointer dereferences or other memory access violations.
+//
+// Call this function immediately before code that might panic to ensure
+// the signal handlers are properly configured for Go's panic recovery mechanism.
+//
+// Example usage:
+//
+// go func() {
+// defer func() {
+// if err := recover(); err != nil {
+// log.Printf("Recovered from panic: %v", err)
+// }
+// }()
+// runtime.ResetSignalHandlers()
+// // Code that might panic...
+// }()
+//
+// Note: This function only has an effect on Linux. On other platforms,
+// it is a no-op.
+func ResetSignalHandlers() {
+ C.fix_all_signals()
+}
diff --git a/v2/pkg/runtime/signal_other.go b/v2/pkg/runtime/signal_other.go
new file mode 100644
index 000000000..3171a700c
--- /dev/null
+++ b/v2/pkg/runtime/signal_other.go
@@ -0,0 +1,18 @@
+//go:build !linux
+
+package runtime
+
+// ResetSignalHandlers resets signal handlers to allow panic recovery.
+//
+// On Linux, WebKit (used for the webview) may install signal handlers without
+// the SA_ONSTACK flag, which prevents Go from properly recovering from panics
+// caused by nil pointer dereferences or other memory access violations.
+//
+// Call this function immediately before code that might panic to ensure
+// the signal handlers are properly configured for Go's panic recovery mechanism.
+//
+// Note: This function only has an effect on Linux. On other platforms,
+// it is a no-op.
+func ResetSignalHandlers() {
+ // No-op on non-Linux platforms
+}
diff --git a/website/docs/guides/linux.mdx b/website/docs/guides/linux.mdx
index 1b55297b5..2cfc2e62a 100644
--- a/website/docs/guides/linux.mdx
+++ b/website/docs/guides/linux.mdx
@@ -70,3 +70,57 @@ If the added package does not resolve the issue, additional GStreamer dependenci
- This issue impacts [Tauri apps](https://tauri.app/).
Source: [developomp](https://github.com/developomp) on the [Tauri discussion board](https://github.com/tauri-apps/tauri/issues/4642#issuecomment-1643229562).
+
+## Panic Recovery / Signal Handling Issues
+
+### App crashes with "non-Go code set up signal handler without SA_ONSTACK flag"
+
+On Linux, if your application crashes with an error like:
+
+```
+signal 11 received but handler not on signal stack
+fatal error: non-Go code set up signal handler without SA_ONSTACK flag
+```
+
+This occurs because WebKit (used for the webview) installs signal handlers that interfere with Go's panic recovery mechanism.
+Normally, Go can convert signals like SIGSEGV (from nil pointer dereferences) into recoverable panics, but WebKit's signal
+handlers prevent this.
+
+### Solution
+
+Use the `runtime.ResetSignalHandlers()` function immediately before code that might panic:
+
+```go
+import "github.com/wailsapp/wails/v2/pkg/runtime"
+
+go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("Recovered from panic: %v", err)
+ }
+ }()
+ // Reset signal handlers right before potentially dangerous code
+ runtime.ResetSignalHandlers()
+
+ // Your code that might panic...
+}()
+```
+
+:::warning Important
+
+- Call `ResetSignalHandlers()` in each goroutine where you need panic recovery
+- Call it immediately before the code that might panic, as WebKit may reset the handlers at any time
+- This is only necessary on Linux - the function is a no-op on other platforms
+
+:::
+
+### Why This Happens
+
+WebKit installs its own signal handlers for garbage collection and other internal processes. These handlers don't include
+the `SA_ONSTACK` flag that Go requires to properly handle signals on the correct stack. When a signal like SIGSEGV occurs,
+Go's runtime can't recover because the signal is being handled on the wrong stack.
+
+The `ResetSignalHandlers()` function adds the `SA_ONSTACK` flag to the signal handlers for SIGSEGV, SIGBUS, SIGFPE, and
+SIGABRT, allowing Go's panic recovery to work correctly.
+
+Source: [GitHub Issue #3965](https://github.com/wailsapp/wails/issues/3965)
diff --git a/website/docs/reference/runtime/intro.mdx b/website/docs/reference/runtime/intro.mdx
index 3c491ecf0..d67e76c64 100644
--- a/website/docs/reference/runtime/intro.mdx
+++ b/website/docs/reference/runtime/intro.mdx
@@ -98,3 +98,46 @@ interface EnvironmentInfo {
arch: string;
}
```
+
+### ResetSignalHandlers
+
+Resets signal handlers to allow panic recovery from nil pointer dereferences and other memory access violations.
+
+Go: `ResetSignalHandlers()`
+
+:::info Linux Only
+
+This function only has an effect on Linux. On macOS and Windows, it is a no-op.
+
+On Linux, WebKit (used for the webview) may install signal handlers without the `SA_ONSTACK` flag, which prevents
+Go from properly recovering from panics caused by nil pointer dereferences (SIGSEGV) or other memory access violations.
+
+Call this function immediately before code that might panic to ensure the signal handlers are properly configured
+for Go's panic recovery mechanism.
+
+:::
+
+#### Example
+
+```go
+go func() {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("Recovered from panic: %v", err)
+ }
+ }()
+ // Reset signal handlers right before potentially dangerous code
+ runtime.ResetSignalHandlers()
+
+ // Code that might cause a nil pointer dereference...
+ var t *time.Time
+ fmt.Println(t.Unix()) // This would normally crash on Linux
+}()
+```
+
+:::warning
+
+This function must be called in each goroutine where you want panic recovery to work, and should be called
+immediately before the code that might panic, as WebKit may reset the signal handlers at any time.
+
+:::