How to Fix 'glm fit Algorithm Did Not Converge' Warning in R

R Published Updated 3 mins read By Leon Wei
How to Fix 'glm fit Algorithm Did Not Converge' Warning in R cover image

Quick summary

Summarize this blog with AI

Introduction

The warning glm.fit: algorithm did not converge says the fitting iteration did not meet its convergence criterion. It does not identify the cause. This lesson focuses on binomial logistic regression and separates an artificially small iteration budget from complete separation, where ordinary maximum-likelihood coefficients have no finite solution.

Tested with R 4.5.1 and detectseparation 0.4.0. Run the code blocks in order. Install the named packages before starting; inputs are supplied below.

Validate the data actually used by the model

Begin with a small overlapping binary-outcome fixture. Missing observations are rejected explicitly rather than silently changing the analysis population.

d <- data.frame(x = rep(-2:2, each = 4),
  y = c(0,0,0,1, 0,0,1,0, 0,1,0,1, 1,1,0,1, 1,1,1,0))
stopifnot(nrow(d) > 0L, all(d$y %in% c(0, 1)),
          all(is.finite(d$x)), length(unique(d$y)) == 2L)
frame <- model.frame(y ~ x, data = d, na.action = na.fail)
X <- model.matrix(y ~ x, frame)
stopifnot(qr(X)$rank == ncol(X))
print(table(d$x, d$y))

Inspect the family/link, response coding, weights and offsets as well as predictors. These checks are specific to this unweighted binary example. A rank-deficient design and separation are different problems.

Reproduce a genuine iteration-limit warning

One iteration is deliberately insufficient. The handler records the warning so the demonstration can continue; it does not make the failed fit usable.

messages <- character(0)
short <- withCallingHandlers(
  glm(y ~ x, data = d, family = binomial(), control = glm.control(maxit = 1)),
  warning = function(w) { messages <<- c(messages, conditionMessage(w)); invokeRestart("muffleWarning") })
stopifnot(!short$converged, any(grepl("algorithm did not converge", messages, fixed = TRUE)))
fit <- glm(y ~ x, data = d, family = binomial(), na.action = na.fail,
           control = glm.control(maxit = 25, epsilon = 1e-8))
stopifnot(fit$converged, all(is.finite(coef(fit))))
print(round(coef(fit), 6))  # intercept 0; x 0.673996
stopifnot(abs(coef(fit)[[2]] - 0.673996) < 1e-6)

Here the larger budget solves the demonstrated problem. R’s glm.control uses a relative deviance-change criterion. A smaller epsilon is a stricter tolerance, not a general convergence cure. A converged fit still needs scientific and predictive validation.

Detect complete separation rather than deleting observations

In this second fixture, x perfectly separates zeros from ones. Increasing the slope always improves the likelihood toward its supremum; a finite maximum-likelihood slope does not exist.

separated <- data.frame(x = c(-3, -2, -1, 1, 2, 3), y = c(0, 0, 0, 1, 1, 1))
limited <- suppressWarnings(glm(y ~ x, data = separated, family = binomial(),
                                control = glm.control(maxit = 10)))
stopifnot(!limited$converged)
diagnostic <- glm(y ~ x, data = separated, family = binomial(),
                  method = detectseparation::detect_separation)
print(coef(diagnostic))  # intercept -Inf; x Inf for this diagnostic
stopifnot(any(is.infinite(coef(diagnostic))))

The diagnostic’s infinite entries identify infinite maximum-likelihood components; they are not fitted effect estimates. detectseparation documents this convention. Numerical convergence alone can be misleading under separation, so inspect the data and separation diagnostic even if more iterations remove a warning.

Choose a remedy for the diagnosed cause

Use a deliberate failure for missing inputs, and preserve an audit of which records a model uses.

bad <- d; bad$x[1] <- NA_real_
stopifnot(inherits(tryCatch(glm(y ~ x, data = bad, family = binomial(),
                              na.action = na.fail), error = identity), "error"))
stopifnot(nobs(fit) == nrow(d))
stopifnot(inherits(tryCatch(glm(y ~ x, data = d[FALSE, ], family = binomial()),
                           error = identity), "error"))

For separation, investigate coding or leakage and consider a justified bias-reduced or penalized logistic model, reporting the changed estimator. Do not delete valid rows, run stepwise selection, or change a link merely to silence the warning. For poor scaling, rescale meaningful predictors and check equivalence; for a small budget, compare iteration progress and stable estimates. Multiple imputation requires fitting and pooling across imputations, not treating the first completed dataset as the complete procedure.

Primary references

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles
How to Remove Outliers in R cover image
r Oct 17, 2026

How to Remove Outliers in R

Learn how to identify and remove outliers in R with this step-by-step guide, featuring detailed code samples for beginners.

How to Use 'countif' in R cover image
r Oct 17, 2026

How to Use 'countif' in R

Unlock the power of 'countif' in R with our comprehensive guide. Perfect for beginners looking to enhance their R programming skills.

How to Use 'abline' in R cover image
r Apr 30, 2024

How to Use 'abline' in R

Unlock the power of 'abline' function in R for data visualization; this guide covers everything from basics to advanced applications with exampl…