Skip to content

WASM: recover panics in compile goroutine to avoid unresolved browser promises#47854

Merged
pelikhan merged 4 commits into
mainfrom
copilot/deep-report-add-recover-to-wasm-compile
Jul 25, 2026
Merged

WASM: recover panics in compile goroutine to avoid unresolved browser promises#47854
pelikhan merged 4 commits into
mainfrom
copilot/deep-report-add-recover-to-wasm-compile

Conversation

Copilot AI commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

compileWorkflow in the WASM entrypoint spawned doCompile in a goroutine without panic recovery. A panic could terminate the WASM runtime path and leave the JS Promise unresolved (browser hang) instead of returning an actionable error.

  • Goroutine panic containment

    • Added a defer-based recover() inside the compile goroutine in cmd/gh-aw-wasm/main.go.
    • Panic values are converted into JS Error objects and routed through reject(...).
  • Promise lifecycle behavior

    • Preserved existing success/error flow (resolve on success, reject on normal compile error).
    • Added explicit panic-to-rejection path so every execution path settles the Promise.
go func() {
	defer handler.Release()
	defer func() {
		if r := recover(); r != nil {
			reject.Invoke(js.Global().Get("Error").New(fmt.Sprintf("compileWorkflow panic: %v", r)))
		}
	}()

	result, err := doCompile(markdown, files, filename)
	if err != nil {
		reject.Invoke(js.Global().Get("Error").New(err.Error()))
		return
	}
	resolve.Invoke(result)
}()

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13.2 AIC · ⌖ 7.42 AIC · ⊞ 7K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.6 AIC · ⌖ 9.99 AIC · ⊞ 7K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add recover to WASM compile goroutine to prevent runtime hang WASM: recover panics in compile goroutine to avoid unresolved browser promises Jul 24, 2026
Copilot AI requested a review from pelikhan July 24, 2026 20:58
@pelikhan
pelikhan marked this pull request as ready for review July 24, 2026 20:58
Copilot AI review requested due to automatic review settings July 24, 2026 20:58
@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

No test files were added or modified in this PR. Test Quality Sentinel skipped.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds panic containment to the WASM compiler so browser promises reject instead of hanging.

Changes:

  • Recovers panics from the compile goroutine.
  • Converts panic values into JavaScript Error rejections.
Show a summary per file
File Description
cmd/gh-aw-wasm/main.go Adds panic-to-Promise rejection handling.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #47854 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The panic recovery is correctly placed and wired. LIFO defer ordering ensures recover() runs before handler.Release(). Every goroutine exit path now settles the Promise. LGTM.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 8.55 AIC · ⌖ 4.45 AIC · ⊞ 5K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Panic recovery in WASM compile goroutine

The fix is correct. Defer order is right (recover fires first, then handler.Release()), and every normal execution path still settles the Promise. One non-blocking edge case is worth documenting.

Finding: silent swallow when resolve.Invoke itself panics

If resolve.Invoke(result) panics after doCompile succeeds (e.g., the JS runtime tears down mid-call), recover() catches the panic and calls reject. The browser ignores the second settlement — the Promise remains resolved and the caller sees the correct outcome. However, the panic is silently swallowed with no observable signal, making it invisible during debugging.

Consider a settled flag to skip the redundant reject:

var settled bool
defer func() {
    if r := recover(); r != nil && !settled {
        reject.Invoke(js.Global().Get("Error").New(fmt.Sprintf("compileWorkflow panic: %v", r)))
    }
}()

result, err := doCompile(markdown, files, filename)
if err != nil {
    reject.Invoke(js.Global().Get("Error").New(err.Error()))
    return
}
settled = true
resolve.Invoke(result)

Non-blocking; does not need to hold up merge.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 · 27.3 AIC · ⌖ 4.58 AIC · ⊞ 5.7K
Comment /review to run again

@github-actions github-actions Bot mentioned this pull request Jul 24, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — the fix is correct and well-scoped; two follow-up improvements are flagged.

📋 Key Themes & Highlights

Key Themes

  • Missing regression test: The panic-recovery path is untested; a future refactor could silently drop the recover() and re-introduce the browser hang.
  • Debuggability: Panic messages omit a stack trace, which would be valuable when diagnosing browser-side failures.

Positive Highlights

  • ✅ Minimal, targeted change — exactly the right scope for a correctness fix.
  • defer ordering is correct: handler.Release() fires after recovery, preventing a use-after-free on the JS func.
  • ✅ All Promise execution paths now settle (resolve, reject on error, reject on panic) — no more browser hangs.
  • ✅ Clear PR description with a before/after code sample.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 31.4 AIC · ⌖ 4.72 AIC · ⊞ 6.7K
Comment /matt to run again

Comment thread cmd/gh-aw-wasm/main.go Outdated

go func() {
defer handler.Release()
defer func() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] No regression test for the panic-recovery path — without one, a future refactor could silently drop the recover() and re-introduce the browser hang.

💡 Suggested approach

Add a test that injects a panic into doCompile and asserts the returned Promise rejects with an Error whose message matches compileWorkflow panic:. Even a Go-level unit test for the inner goroutine body via a thin wrapper would document the intent and catch regressions.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in fa765f0: I extracted the goroutine settlement path into runCompileWithRecovery and added cmd/gh-aw-wasm/compile_recovery_test.go with explicit success/error/panic regression tests so this path is now covered.

Comment thread cmd/gh-aw-wasm/main.go Outdated
defer handler.Release()
defer func() {
if r := recover(); r != nil {
reject.Invoke(js.Global().Get("Error").New(fmt.Sprintf("compileWorkflow panic: %v", r)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] The panic message prefix "compileWorkflow panic: " is helpful, but consider also including a stack trace to make post-mortem debugging easier. Go's runtime/debug.Stack() can be captured at recover-time before the goroutine unwinds.

💡 Suggested addition
import "runtime/debug"

if r := recover(); r != nil {
    stack := debug.Stack()
    reject.Invoke(js.Global().Get("Error").New(
        fmt.Sprintf("compileWorkflow panic: %v\n%s", r, stack),
    ))
}

Browsers surface the full Error.message in DevTools, so the stack trace will be visible to developers without any additional tooling.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in fa765f0: panic rejection now includes a stack trace via runtime/debug.Stack() (compileWorkflowPanicError), and the panic-path test asserts the rejection includes both the panic prefix and stack context.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, confirm the branch is up to date, address any still-unresolved review feedback, rerun the failed checks below, and push fixes as needed.

Failed checks:

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13.2 AIC · ⌖ 7.42 AIC · ⊞ 7K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot July 24, 2026 21:46
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, confirm the branch is up to date, address unresolved review feedback, and rerun or retrigger checks on the latest HEAD.

Unresolved review feedback:

Run: https://github.com/github/gh-aw/actions/runs/30135795800

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.6 AIC · ⌖ 9.99 AIC · ⊞ 7K ·
Comment /souschef to run again

@github-actions

Copy link
Copy Markdown
Contributor

Triage | Category: bug | Risk: medium | Score: 68/100 (impact:30, urgency:23, quality:15)

Recommended action: fast_track — WASM panic recovery in compile goroutine prevents unresolved browser promises; small focused change (+107/-7), CI passing.

Score breakdown: impact=30 (browser/WASM stability), urgency=23 (CI green, active area), quality=15 (CI pass, test included)

Generated by 🔧 PR Triage Agent · sonnet46 · 31.3 AIC · ⌖ 5.51 AIC · ⊞ 5.7K ·

@pelikhan
pelikhan merged commit 066115a into main Jul 25, 2026
24 checks passed
@pelikhan
pelikhan deleted the copilot/deep-report-add-recover-to-wasm-compile branch July 25, 2026 02:45
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.3

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[deep-report] Add recover() to WASM compile goroutine to prevent browser runtime hang

4 participants