{"record":{"id":"3526704c956fe1f9","repo":"vitessio/vitess","slug":"preflight-mysqld-shutdown-hook-failed-v","errorCode":null,"errorMessage":"preflight_mysqld_shutdown hook failed: %v","messagePattern":"preflight_mysqld_shutdown hook failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/mysqlctl/mysqld.go","lineNumber":801,"sourceCode":"\t\t}\n\t\tdefer releaseFlock()\n\t}\n\n\t// possibly mysql is already shutdown, check for a few files first\n\tif mysqldAlreadyStopped(cnf) {\n\t\tlog.Warn(\"assuming mysqld already shut down - no socket, no pid file found\")\n\t\treturn nil\n\t}\n\n\t// try the preflight mysqld shutdown hook, if any\n\th := hook.NewSimpleHook(\"preflight_mysqld_shutdown\")\n\thr := h.ExecuteContext(ctx)\n\tswitch hr.ExitStatus {\n\tcase hook.HOOK_SUCCESS, hook.HOOK_DOES_NOT_EXIST:\n\t\t// hook exists and worked, or else does not exist.\n\tdefault:\n\t\t// hook failed, we report error\n\t\treturn fmt.Errorf(\"preflight_mysqld_shutdown hook failed: %v\", hr.String())\n\t}\n\n\treturn mysqld.shutdownWithReplicaCrashSafety(ctx, preparationBudget, func() (bool, error) {\n\t\treturn mysqld.executeShutdown(ctx, cnf, waitForMysqld, shutdownTimeout)\n\t})\n}\n\n// replicaShutdownPreparationBudget returns how long the replica crash-safety\n// preparation may take for the given shutdown timeout. A zero (or negative)\n// shutdown timeout means the caller wants an immediate, no-wait shutdown\n// (mysqladmin --shutdown-timeout=0): honor that by granting the preparation\n// no budget at all rather than its default one.\nfunc replicaShutdownPreparationBudget(shutdownTimeout time.Duration) time.Duration {\n\tif shutdownTimeout <= 0 {\n\t\treturn 0\n\t}\n\treturn min(replicaShutdownPreparationTimeout, shutdownTimeout)\n}","sourceCodeStart":783,"sourceCodeEnd":819,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/mysqlctl/mysqld.go#L783-L819","documentation":"Before shutting down mysqld, Vitess runs an optional user-supplied 'preflight_mysqld_shutdown' hook. If the hook exits with any status other than HOOK_SUCCESS or HOOK_DOES_NOT_EXIST, Shutdown aborts before stopping mysqld and returns this error containing the hook's result string. It is a deliberate extension point failure — the operator's pre-shutdown validation refused the shutdown.","triggerScenarios":"Mysqld.Shutdown executes the preflight_mysqld_shutdown hook via hook.ExecuteContext; the hook script exits non-zero (not 0/SUCCESS and not DOES_NOT_EXIST), e.g. exit status 1 from a custom guard script.","commonSituations":"A site-specific preflight script checks replication lag or backup state and fails on a laggy replica; the hook script itself has a bug or wrong interpreter path; the hook returns an unexpected status code such as HOOK_PARTIAL_SUCCESS being treated as failure.","solutions":["Inspect hr in the error message for the hook's exit status, stdout and stderr to see why the preflight script failed.","Fix the condition the preflight hook checks (e.g. wait for replica lag to drain, take a backup) and retry the shutdown.","Correct or disable the hook script if it is malfunctioning (bad shebang, wrong exit code conventions).","Remember hooks returning HOOK_DOES_NOT_EXIST are fine — only a real non-success exit blocks shutdown."],"exampleFix":"// before\n#!/bin/sh\nexit 1  # debugging leftover\n// after\n#!/bin/sh\n# verify replica is caught up\nmysql -e 'SELECT 1' || exit 1\nexit 0","handlingStrategy":"validation","validationCode":"hr := hook.NewHook(\"preflight_mysqld_shutdown\").ExecuteContext(ctx)\nif hr.ExitStatus != hook.HOOK_SUCCESS && hr.ExitStatus != hook.HOOK_DOES_NOT_EXIST {\n    log.Infof(\"preflight would fail: %s\", hr.String())\n}","typeGuard":"func hookSucceeded(hr *hook.HookResult) bool {\n    return hr.ExitStatus == hook.HOOK_SUCCESS || hr.ExitStatus == hook.HOOK_DOES_NOT_EXIST\n}","tryCatchPattern":"if err := mysqld.Shutdown(ctx, cnf, true, timeout); err != nil && strings.Contains(err.Error(), \"preflight_mysqld_shutdown hook failed\") {\n    // read hr details from message, resolve the blocking condition, retry\n}","preventionTips":["Dry-run preflight hooks manually before wiring them into production shutdown paths.","Use only documented hook exit codes (0 success); avoid ad-hoc non-zero codes.","Keep preflight hooks fast and idempotent.","Log hook stdout/stderr to a persistent location for postmortems."],"tags":["mysql","shutdown","hooks","preflight","extension-point"],"backgroundTag":"hook-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}