{"record":{"id":"b0581e4159917660","repo":"juicedata/juicefs","slug":"tikv-client-txn-func-error-v","errorCode":null,"errorMessage":"tikv client txn func error: %v","messagePattern":"tikv client txn func error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/meta/tkv_tikv.go","lineNumber":374,"sourceCode":"}\n\nfunc (c *tikvClient) txn(ctx context.Context, f func(*kvTxn) error, retry int) (err error) {\n\tvar opts []tikv.TxnOption\n\tif val := ctx.Value(txSessionKey{}); val != nil {\n\t\topts = append(opts, tikv.WithStartTS(val.(uint64)))\n\t}\n\n\ttx, err := c.client.Begin(opts...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tfe, ok := r.(error)\n\t\t\tif ok {\n\t\t\t\terr = fe\n\t\t\t} else {\n\t\t\t\terr = errors.Errorf(\"tikv client txn func error: %v\", r)\n\t\t\t}\n\t\t}\n\t}()\n\tif err = f(&kvTxn{&tikvTxn{tx}, retry}); err != nil {\n\t\treturn err\n\t}\n\tif !tx.IsReadOnly() {\n\t\ttx.SetEnable1PC(true)\n\t\ttx.SetEnableAsyncCommit(true)\n\t\terr = tx.Commit(ctx)\n\t}\n\treturn err\n}\n\nfunc (c *tikvClient) scan(prefix []byte, handler func(key, value []byte) bool) error {\n\tend := nextKey(prefix)\n\tstart := prefix\nOUT:","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/pkg/meta/tkv_tikv.go#L356-L392","documentation":"This error is produced by the TikV transaction wrapper in pkg/meta/tkv_tikv.go when the closure passed to the transaction helper panics with a non-error value. The deferred recover() catches the panic; if the recovered value is not an `error`, it wraps it into this formatted error. It signals an unexpected panic inside a TiKV transaction callback rather than a normal transaction failure.","triggerScenarios":"A transaction closure `f(&kvTxn{...})` panics with a non-error value, e.g. nil pointer dereference, index out of range, or an explicit panic(string) inside code executed via tkv txn on TiKV metadata engine.","commonSituations":"Bugs in metadata engine code paths (e.g. nil map or slice access while decoding a key), panics from third-party TiKV client libraries during txn commit, or a user-supplied/patched function that panics with a string value instead of an error.","solutions":["Inspect the wrapped %v message to identify the actual panic value and the stack trace printed by the recover path","Fix the nil-pointer or index bug in the transaction closure that panicked","If a library panics with a string, convert it to an error at the source or wrap it deliberately with errors.Errorf","Update the TiKV client library if the panic originates inside it and is a known fixed issue"],"exampleFix":"// before\npanic(\"bad key format\")\n// after\nreturn errors.Errorf(\"bad key format: %q\", key)","handlingStrategy":"try-catch","validationCode":"// Go: ensure txn closure arguments are non-nil before calling\nif key == nil || txn == nil { return errors.New(\"invalid txn input\") }","typeGuard":"if fe, ok := r.(error); ok { err = fe } else { err = errors.Errorf(\"tikv client txn func error: %v\", r) }","tryCatchPattern":"err := doTxn(func(t *kvTxn) error {\n    defer func() { if r := recover(); r != nil { err = errors.Errorf(\"txn panic: %v\", r) } }()\n    return t.Set(key, val)\n})","preventionTips":["Never call panic with non-error values in metadata code paths","Nil-check keys/values before txn operations","Add -race and panic-recovery tests for txn closures","Keep the TiKV client library up to date"],"tags":["tikv","panic","metadata","go"],"backgroundTag":"internal-invariant-violation","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}