{"record":{"id":"802df02dc45571af","repo":"redis/go-redis","slug":"errors-new-cmd-val-dynamic-the-server-s-shutdow","errorCode":null,"errorMessage":"errors.New(cmd.val) (dynamic: the server's shutdown reason reply is wrapped as the error)","messagePattern":"errors\\.New\\(cmd\\.val\\) \\(dynamic: the server's shutdown reason reply is wrapped as the error\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"commands.go","lineNumber":767,"sourceCode":"}\n\nfunc (c cmdable) shutdown(ctx context.Context, modifier string) *StatusCmd {\n\tvar args []interface{}\n\tif modifier == \"\" {\n\t\targs = []interface{}{\"shutdown\"}\n\t} else {\n\t\targs = []interface{}{\"shutdown\", modifier}\n\t}\n\tcmd := NewStatusCmd(ctx, args...)\n\t_ = c(ctx, cmd)\n\tif err := cmd.Err(); err != nil {\n\t\tif err == io.EOF {\n\t\t\t// Server quit as expected.\n\t\t\tcmd.err = nil\n\t\t}\n\t} else {\n\t\t// Server did not quit. String reply contains the reason.\n\t\tcmd.err = errors.New(cmd.val)\n\t\tcmd.val = \"\"\n\t}\n\treturn cmd\n}\n\nfunc (c cmdable) Shutdown(ctx context.Context) *StatusCmd {\n\treturn c.shutdown(ctx, \"\")\n}\n\nfunc (c cmdable) ShutdownSave(ctx context.Context) *StatusCmd {\n\treturn c.shutdown(ctx, \"save\")\n}\n\nfunc (c cmdable) ShutdownNoSave(ctx context.Context) *StatusCmd {\n\treturn c.shutdown(ctx, \"nosave\")\n}\n\n// SlaveOf sets a Redis server as a replica of another, or promotes it to being a master.","sourceCodeStart":749,"sourceCodeEnd":785,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/commands.go#L749-L785","documentation":"When the SHUTDOWN command (Shutdown/ShutdownSync) gets a string reply instead of the connection closing, the server did not quit; the reply text is wrapped as the command's error via errors.New(cmd.val). The error message is dynamic — it is whatever reason the server replied with.","triggerScenarios":"Calling rdb.Shutdown(ctx) or rdb.ShutdownSync(ctx) when the server refuses to shut down, e.g. a failing RDB save blocking SHUTDOWN without SAVE/NOSAVE, or a permission refusal; the string reply carries the reason.","commonSituations":"Scripting shutdown of instances where a BGSAVE child failed (Redis refuses SHUTDOWN with save pending unless SAVE/NOSAVE is given); connection user lacks SHUTDOWN privileges.","solutions":["Inspect the error message — it is the server's own reason.","Retry with ShutdownNoSave(ctx) (SHUTDOWN NOSAVE) if a failing persistence save is blocking shutdown.","Check server logs for the underlying persistence/replication failure.","Verify the connection user is allowed to run SHUTDOWN."],"exampleFix":"// before\nerr := rdb.Shutdown(ctx).Err() // opaque server reason\n// after\nif err := rdb.Shutdown(ctx).Err(); err != nil {\n    // fall back to skipping the save\n    err = rdb.ShutdownNoSave(ctx).Err()\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := rdb.Shutdown(ctx).Err(); err != nil {\n    // err message is the server's own reason string\n    log.Printf(\"server refused shutdown: %v\", err)\n    if err2 := rdb.ShutdownNoSave(ctx).Err(); err2 != nil {\n        log.Fatalf(\"shutdown failed: %v\", err2)\n    }\n}","preventionTips":["Prefer ShutdownNoSave when a failing RDB save may block shutdown.","Expect the connection to close on success — any string reply means refusal.","Check server logs alongside the returned reason."],"tags":["shutdown","server-refusal","dynamic-error"],"backgroundTag":"redis-server-command-refused","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}