geektutu/7days-golang · error

panic(p)

Error message

panic(p)

What it means

In Transaction (day6-transaction), the deferred recover handler re-throws any panic (panic(p)) after rolling back the transaction. This is not an error the library invents — it preserves an original panic that occurred inside the transaction callback (e.g. an 'invalid sql type' schema panic or a nil-map panic) while guaranteeing Rollback ran first.

Source

Thrown at gee-orm/day6-transaction/geeorm.go:66

// NewSession creates a new session for next operations
func (engine *Engine) NewSession() *session.Session {
	return session.New(engine.db, engine.dialect)
}

// TxFunc will be called between tx.Begin() and tx.Commit()
// https://stackoverflow.com/questions/16184238/database-sql-tx-detecting-commit-or-rollback
type TxFunc func(*session.Session) (interface{}, error)

// Transaction executes sql wrapped in a transaction, then automatically commit if no error occurs
func (engine *Engine) Transaction(f TxFunc) (result interface{}, err error) {
	s := engine.NewSession()
	if err := s.Begin(); err != nil {
		return nil, err
	}
	defer func() {
		if p := recover(); p != nil {
			_ = s.Rollback()
			panic(p) // re-throw panic after Rollback
		} else if err != nil {
			_ = s.Rollback() // err is non-nil; don't change it
		} else {
			err = s.Commit() // err is nil; if Commit returns error update err
		}
	}()

	return f(s)
}

View on GitHub (pinned to cf36443821)

Solutions

  1. Look at the original panic value/stack trace chained below this re-throw — the root cause is inside the transaction callback or the schema
  2. Fix the underlying panic source (e.g. unsupported field type in the model)
  3. If you need to convert panics to errors, wrap the s.Transaction call in your own recover at the caller level

Example fix

// before
_ = db.Transaction(func(s *geeorm.Session) error {
    return s.Raw(...).Find(&users) // may panic on bad schema
})
// after
func safeTx(db *geeorm.Engine) (err error) {
    defer func() { if p := recover(); p != nil { err = fmt.Errorf("tx panic: %v", p) } }()
    return db.Transaction(func(s *geeorm.Session) error {
        return s.Raw(...).Find(&users)
    })
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the model is schema-valid before entering the transaction
if err := checkSchemaTypes(&Order{}); err != nil {
    return err
}
return db.Transaction(func(s *geeorm.Session) error { ... })

Try / catch

func runTx(db *geeorm.Engine) (err error) {
    defer func() {
        if p := recover(); p != nil {
            err = fmt.Errorf("transaction panicked (rolled back): %v", p)
        }
    }()
    return db.Transaction(func(s *geeorm.Session) error {
        // transaction body
        return nil
    })
}

Prevention

When it happens

Trigger: Any panic raised inside the transaction's function body or its deferred chain: e.g. DataTypeOf panicking on an unsupported field type, a nil pointer in the callback, or Commit-side panics — all surface as this re-thrown panic after rollback.

Common situations: A schema bug (like error 107's invalid sql type) occurring mid-transaction; user callback code dividing by zero or indexing nil; bugs in hook code executed inside the transaction.

Related errors


AI-assisted analysis of geektutu/7days-golang@cf36443821 (2026-09-03). Data as JSON: /api/errors/0ef0169a3ebff1c6. Report an issue: GitHub.