wavetermdev/waveterm · error

TermRef not current

Error message

TermRef not current

What it means

TermRef implements io.Writer by forwarding data to a terminal block via TermWrite. It refuses to write unless the underlying VDomRef has a currently-mounted/active terminal instance (tracked by the HasCurrent atomic flag). Writing to a stale, detached, or not-yet-mounted terminal ref yields this error instead of silently dropping bytes.

Source

Thrown at tsunami/app/hooks.go:42

func UseVDomRef() *vdom.VDomRef {
	rc := engine.GetGlobalRenderContext()
	val := engine.UseVDomRef(rc)
	refVal, ok := val.(*vdom.VDomRef)
	if !ok {
		panic("UseVDomRef hook value is not a ref (possible out of order or conditional hooks)")
	}
	return refVal
}

// TermRef wraps a VDomRef and implements io.Writer by forwarding writes to the terminal.
type TermRef struct {
	*vdom.VDomRef
}

// Write implements io.Writer by sending data to the terminal via TermWrite.
func (tr *TermRef) Write(p []byte) (n int, err error) {
	if tr.VDomRef == nil || !tr.VDomRef.HasCurrent.Load() {
		return 0, fmt.Errorf("TermRef not current")
	}
	err = TermWrite(tr.VDomRef, string(p))
	if err != nil {
		return 0, err
	}
	return len(p), nil
}

// TermSize returns the current terminal size, or nil if not yet set.
func (tr *TermRef) TermSize() *vdom.VDomTermSize {
	if tr.VDomRef == nil {
		return nil
	}
	return tr.VDomRef.TermSize
}

// UseTermRef returns a TermRef that can be passed as a ref to "wave:term" elements
// and also implements io.Writer for writing directly to the terminal.

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check tr.VDomRef != nil and tr.VDomRef.HasCurrent.Load() before writing, and re-acquire a fresh ref if stale
  2. Retry after the component mounts (wait for HasCurrent to become true)
  3. Route writes through a channel/queue drained only while the ref is current
  4. Treat the error as 'terminal gone' and stop writing rather than retrying forever

Example fix

// before
n, err := termRef.Write(data)
// after
if termRef.VDomRef == nil || !termRef.VDomRef.HasCurrent.Load() {
    return // terminal not attached; skip or buffer
}
n, err := termRef.Write(data)
Defensive patterns

Strategy: validation

Validate before calling

func canWrite(tr *tsunami.TermRef) bool {
    return tr != nil && tr.VDomRef != nil && tr.VDomRef.HasCurrent.Load()
}
if canWrite(tr) {
    tr.Write(data)
}

Type guard

func isCurrentTermRef(tr *tsunami.TermRef) bool {
    return tr != nil && tr.VDomRef != nil && tr.VDomRef.HasCurrent.Load()
}

Try / catch

n, err := tr.Write(p)
if err != nil && err.Error() == "TermRef not current" {
    // buffer or drop; terminal detached
}

Prevention

When it happens

Trigger: Calling TermRef.Write (directly or via fmt.Fprintf/io.WriteString) when tr.VDomRef is nil, or when the vdom ref exists but HasCurrent is false — e.g. before the block's component mounts the ref, after the block is closed/destroyed, or after a re-render replaced the ref.

Common situations: Writing terminal output from a background goroutine after the UI block was closed; writing during startup before the component attached; holding a TermRef across a block close/reopen cycle.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/a828d45c1084be1a. Report an issue: GitHub.