uber-go/zap · error

ce.Message

Error message

ce.Message

What it means

CheckWriteAction.OnWrite implements the post-write hook: if the action is WriteThenPanic, it panics with the checked entry's message after the log entry has been written. This is the configured behavior for Panic-level logging — the message logged is also the panic value.

Source

Thrown at zapcore/entry.go:196

	// WriteThenNoop indicates that nothing special needs to be done. It's the
	// default behavior.
	WriteThenNoop CheckWriteAction = iota
	// WriteThenGoexit runs runtime.Goexit after Write.
	WriteThenGoexit
	// WriteThenPanic causes a panic after Write.
	WriteThenPanic
	// WriteThenFatal causes an os.Exit(1) after Write.
	WriteThenFatal
)

// OnWrite implements the OnWrite method to keep CheckWriteAction compatible
// with the new CheckWriteHook interface which deprecates CheckWriteAction.
func (a CheckWriteAction) OnWrite(ce *CheckedEntry, _ []Field) {
	switch a {
	case WriteThenGoexit:
		runtime.Goexit()
	case WriteThenPanic:
		panic(ce.Message)
	case WriteThenFatal:
		exit.With(1)
	}
}

var _ CheckWriteHook = CheckWriteAction(0)

// CheckPreWriteHook is a function that transforms an Entry and its Fields
// before they are written to cores. Register one on a CheckedEntry with the
// Before method.
//
// Pre-write hooks run in the order they were added, before any Core's Write
// method is called. They may modify the Entry and Fields freely.
type CheckPreWriteHook func(Entry, []Field) (Entry, []Field)

// CheckedEntry is an Entry together with a collection of Cores that have
// already agreed to log it.
//

View on GitHub (pinned to bbd4ecbd87)

Solutions

  1. If the panic is unintended, log with logger.Error instead of logger.Panic, or configure the level action as WriteThenFatal/WriteThenNoop.
  2. Wrap entry points (HTTP handlers, goroutine bodies) with defer recover() where Panic-level logging is expected.
  3. If you need the panic value, recover and type-assert — it is exactly the string passed to logger.Panic.

Example fix

// before
logger.Panic("cache unavailable") // panics process
// after
logger.Error("cache unavailable")
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer Error level if panicking is undesired:
logger.Error("cache unavailable") // instead of logger.Panic(...)

Try / catch

func safeHandler(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if rec := recover(); rec != nil {
                if msg, ok := rec.(string); ok {
                    logger.Error("recovered panic-level log", zap.String("msg", msg))
                }
                http.Error(w, "internal error", 500)
            }
        }()
        h(w, r)
    }
}

Prevention

When it happens

Trigger: Any logger call at Panic level (logger.Panic(...), logger.Check(zapcore.PanicLevel, msg).Write(...)) with a core/hook configured with CheckWriteAction(WriteThenPanic) — the write completes, then panic(ce.Message) runs.

Common situations: Applications using zap's Panic-level for unrecoverable conditions, relying on a recover() in a top-level handler (HTTP middleware, worker loops) to convert the panic into a 500/retry; panics escaping tests when recover isn't set.

Related errors


AI-assisted analysis of uber-go/zap@bbd4ecbd87 (2026-08-31). Data as JSON: /api/errors/94adfd4d52030bcd. Report an issue: GitHub.