- Released on CRAN: 14 March 2023
- New features and user-visible changes:
- this is just a maintenance release
- estimator = “js” (james-stein) and “jsa” (james-stein aggregated) for (simple) CFA model only.

- Bugs/glitches discovered after the release:
- (just released)

- Released on CRAN: 9 Feb 2023
- New features and user-visible changes:
- this is mostly a bug-fix release
- a new argument ov.order= can be set to “model” (= the default) or “data”; this determines if the internal order of the observed variable names (as reflected in the output of lavNames()) are based on the model syntax or the data
- add new tests: browne.residual.nt.model and browne.residual.adf.model
- initial support (point estimates only) for some non-iterative estimators for (simple) CFA models: estimator = “fabin2”, “fabin3”, “guttman1952”, “bentler1982”
- robust RMSEA/CFI values in the categorical setting will result in NA values if the input (polychoric/tetrachoric) matrix is not positive-definite (use fm.args = list(cat.check.pd = FALSE) to override)
- lavCor() now accepts a logical TRUE/FALSE value for the ordered= argument

- Bugs/glitches discovered after the release:
- (none yet)

- Released on CRAN: 9 Jan 2023
- New features and user-visible changes:
- a new function efa() allows for classic (single-group) explorary factor analysis
- a new function lavPredictY() allows for the prediction of ‘y’ values based on ‘x’ values (see ?lavPredictY for a reference)
- a new function lavTest() allows to extract (or compute) one or several test statistics based on an already fitted object
- new test statistics: browne.residual.adf and browne.residual.nt
- summary() and fitMeasures() gain an fm.args= argument, allowing to set options (e.g., rmsea.ci.level) related to fit measures
- lavTestLRT() will now provide the RMSEA based on the difference test
- robust CFI and RMSEA are now computed for more settings: when (all) data is categorical/ordered, when data is continuous but the estimator is MLMV, and when missing = “ml” (based on work done by Victoria Savalei and colleagues)
- new global option scaled.test= determines which test statistic will be scaled (if multiple test statistics have been requested)
- new option gamma.unbiased: if set TRUE, an unbiased version of the so-called Gamma matrix will be used, instead of the regular (biased) version. See this paper
- iseed now works correctly in bootstrapLavaan() and related functions thanks to a patch provided by Shu Fai Cheung
- bootstrapLavaan() now always returns all bootstrap runs (not just the converged ones); it also warns about non-admissible solutions
- when bounds = “standard” or “wide”, bounds are now also computed for covariances per default
- the output of lavInspect() for “gamma”, “wls.v”, “wls.obs” and “wls.est” now includes labels
- the sam() function gains alpha.correction= argument to allow for small sample corrections

- Important bug fix:
- in all lavaan versions < 0.6-13, there was on error in the computation of scaled (difference) test statistics if 1) a mean and variance adjusted test was used (i.e., test = “scaled.shifted”, or test = “mean.var.adjusted”), 2) multiple groups were involved, and 3) equality constraints (across groups) were involved. A typical setting are measurement invariance tests when data is categorical. In those settings, the resulting test statistic was slightly off (typically <5%). Many thanks to Steffen Gronneberg, Njal Foldnes and Jonas Moss for reporting this.

- Bugs/glitches discovered after the release:
- an error is produced when you have missing = “ml”, a robust test statistic, a model with exogenous covariates, fixed.x = TRUE (=the default), and you request fit measures (quickfix: use fixed.x = FALSE) (see github issue 261)
- lavPredictY() did not work properly when newdata= was used, and meanstructure = FALSE; in addition, the columns in the output could be in the wronger order if the order of ynames was different than the order of lavNames() (see github issue 259)
- the NACOV and WLS.V matrices (if provided as arguments) naively assume that the internal order of the observed variables is the same as the order of the sample statistics (see github issue 260)

- Released on CRAN: 4 July 2022
- New features and user-visible changes:
- twolevel SEM now also supports conditional.x = TRUE
- out <- summary(fit) is now completely silent (i.e., nothing is printed); out contains a list with ingredients (github issue 193)
- parameterEstimates() gains boot.ci.type = “bca” (for the single-group setting only, for now)
- syntax: allow for partial specifications of thresholds (github issue 215)
- lavaanify() gains a nthresholds= argument (see github issue 214)
- when bootstrapping is used, a warning is printed informing the user how many solutions were not admissible (see github issue 235)

- Bugs/glitches discovered after the release:
- standardizedSolution() did not work if representation = “RAM”
- iseed did not work properly in bootstrapLavaan() and other bootstrap routines
- the sam() function did not work (any longer) with categorical data
- the scaled (difference) mean-and-variance adjusted test statistic is wrong in the case of multiple groups involving equality constraints

- Released on CRAN: 31 March 2022
- New features and user-visible changes:
- this is (again) only a maintenance release to prepare for R 4.2
- new option ceq.simple; when set to TRUE, a more compact representation is used in the parameter table if only simple equality constraints are used; the default is FALSE (for now)

- Bugs/glitches discovered after the release:
- ridge= option did not work properly
- df counting bug when nlevels > 1L, ngroups > 1L and fixed.x = TRUE
- EPC decisions were not based on the absolute value (pull request 231)
- name clash between ‘cl’ argument and ‘cluster’ argument
- interaction terms defined by a colon (a:b) were included in lavNames(,”ov.x”) even if either a or b is a dependent variable
- standardizedSolution() did not work if representation = “RAM”

- Released on CRAN: 25 January 2022
- New features and user-visible changes:
- this is a maintenance release, with mostly minor changes under the hood (the github issues >214 were not yet addressed in this release)
- initial support for representation = “RAM”
- better starting values for regression coefficients, when all variables are observed
- sam(): an indicator can now also be a predictor in the structural part
- estimator DLS: allow for non-positive Gamma, and allow sample.cov.rescale to be set by the user
- new rotation criteria: bi-quartimin and bi-geomin

- Bugs/glitches discovered after the release:
- if all data is missing for a pair of variables, the degrees of freedom should be adapted

- Released on CRAN: 27 June 2021
- New features and user-visible changes:
- the sam() function is now public
- initial support for multilevel + missing = “ml”; only for estimator = “ML” (not “MLR”)
- if missing = “two-stage”, h1.information is now always set to “unstructured”
- group.w.free = TRUE now also works for DWLS/WLS/WLSMV estimators

- Bugs/glitches discovered after the release:
- multilevel+missing=”ml” did not work if there was only 1 variabele on the within level
- see github issues >214

- Released on CRAN: 10 March 2021
- New features and user-visible changes:
- new estimator = “DLS” for distributionally-weighted least squares (see Du, H., & Bentler, P.M. (in press). Distributionally-weighted least squares in structural equation modeling. Psychological Methods.)
- new optimizer: optim = “GN” provides Gauss-Newton optimization
- group.w.free = TRUE now also works in the categorical case
- the information=, observed.information= and h1.information= arguments now accept a vector of two elements, eg information = c(“observed”, “expected”); the first entry is used for the standard errors, while the second entry is used for the test statistic
- new arguments omega.information=, omega.h1.information=, omega.information.mean=, and omega.h1.information.meat= allow for even more variants of various robust test statistics
- test = “yuan.bentler” now always uses observed.information = “h1” (if information is observed) and omega.h1.information = “unstructured”
- lavResiduals() has a new output = “table” argument
- rotations.se = “bordered” per default (only used when rotation is used)
- in a multiple group analysis, a single modifier is always recycled accross; this is now also true for labels (with a warning, unless group.equal= is used)

- Known issues:
- same as 0.6-1

- Bugs/glitches discovered after the release:
- standardizedSolution() did not show labels (if any)
- lavResiduals() gave wrong results when the solution was rotated
- lav_mvnorm_missing_h1.R sometimes failed to produce starting values (and stopped with an error)
- a check to force the (co)variance matrix of standardized parameters to be positive definite was too strict (and is now omitted)
- lavTestLRT + satorra2002 + A.method = “exact” did not work when equality constraints were involved (github issue 211)

- Released on CRAN: 31 July 2020
- New features and user-visible changes:
- latent ~~ observed formulas are now supported; the observed variables are automatically upgraded to (single-indicator) latent variables
- new option check.lv.names: when set to FALSE, lavaan proceeds even if it detects that some latent variable names occur in the dataset
- optimization is made somewhat more robust: if a first attempt fails (no convergence) three additional attempts are made (with optim.parscale= “standardize”, with start = “simple”, and with the combination optim.parscale = “standardized” and start = “simple”)
- the second argument of lavPredict() is now newdata (instead of type), to be consistend with predict()

- Known issues:
- same as 0.6-1

- Bugs/glitches discovered after the release:
- fitMeasures() did not longer work for estimator = “PML”
- lavPredict + newdata + categorical did not work
- lavCor() did not listen to the missing = “fiml” argument
- lavPredict + Bartlett + missing: if all indicators of a factor are missing, the factor score was 0 (should be NA)
- block syntax for groups was no longer working

- Released on CRAN: 13 May 2020
- New features and user-visible changes:
- lavPredict() + se = TRUE now works correctly in the presence of missing data and missing = “fiml”
- lavResiduals() + summary = TRUE now provides confidence intervals and better labelling
- lavResiduals() + summary = TRUE now works for categorical data, but only in models without exogenous covariates (for now)
- lavCor() gains cor.smooth= argument to force the correlation matrix to be positive definite
- sum(sampling.weights) no longer needs to equal the total sample size
- sampling.weights can now be used in combination with categorical data and estimator WLSMV and friends
- information/h1.information/observed.information arguments now accept a vector of two elements: the first one is for the standard errors, the second for the test statistic
- the data (provided by the data= argument) is now always converted to a data.frame (in case it is a tibble, or another class that inherits from data.frame)
- experimental feature: when the sample size is (very) small, adding the argument bounds = TRUE may stabilize estimation, by automatically choosing upper and lower bounds for several sets of model parameters

- Known issues:
- same as 0.6-1

- Bugs/glitches discovered after the release:
- lavPredict + method=”Bartlett” + fsm=TRUE failed
- lavPredict + newdata failed if newdata contained a single observation
- parallel= option did not work in boostrapLavaan()
- standardizedSolution + type = “std.lv” failed
- missing = “ml” (or “fiml”) + sampling.weights failed
- lavaan:::fsr() failed, unless missing = “listwise” was given explicitly

- Released on CRAN: 28 Aug 2019
- New features and user-visible changes:
- a new option `effect.coding’ can be used to scale the latent variables; a typical use is effect.coding = “loadings”, or effect.coding = c(“loadings”, “intercepts”); if “loadings” is included, equality constraints are used so that the average of the factor loadings (per latent variable) equals 1; if “intercepts” is included, equality constraints are used so that the sum of the intercepts (belonging to the indicators of a single latent variable) equals zero; as a shortcut, you can also set effect.coding = TRUE, which implies effect.coding = c(“loadings”, “intercepts”); note that his may not work well with bifactor models, or any other type of model where items depend on multiple factors
- summary(fit, nd = 4) now shows 4 digits (after the decimal point) for all (non-integer) numbers, including the header; the header has been re-arranged slightly: the estimator is now shown on the top; the test statistic(s) are now in a section ‘Model Test User Model:’
- the test= argument now accepts a vector of multiple test statistics, for example test = c(“satorra.bentler”, “mean.var.adjusted”)
- parameterEstimates(), standardizedSolution() and fitMeasures() have a new output= argument which can be set to “text” for pretty printing

- Known issues:
- same as 0.6-1

- Bugs/glitches discovered after the release:
- in the categorical setting (estimator U/WLSMV) + missing = “pairwise” + the number of thresholds is larger than one: some entries in the weight matrix are not correct, resulting in (small) bias for some parameters
- optim.method=”none” is ignored in nlminb.constr() settings; as a side-effect, this produces wrong results in lavTestLRT() if method=”satorra.bentler.2010”, and explicit inequality constraints are used in the model
- wrong labels for lavInspect(,”lv.mean”) and lavInspect(,”cov.all”) if both formative and reflexive latent variables are used
- the “<~” operator did not work well in the categorical setting in combination with conditional.x = TRUE
- parameterEstimates + fmi = TRUE often gives crazy results
- lavTestLRT + MLR + df=0 for one of the models results in an error
- estimator = “WLS” + continuous data + missing = “pairwise” does not work
- loglikelood value if fixed.x = TRUE and only sample statistics are provided is still reflecting the joint loglikelihood

- Released on CRAN: 3 July 2019
- New features and user-visible changes:
- this release includes mostly changes under the hood
- an extra check is done for convergence (even if the optimizer claims convergence): we check if all the elements of the (unscaled) gradient are smaller than 0.0001 (in absolute value). If not, we set converged = FALSE. The tolerance value 0.0001 can be changed by the option optim.dx.tol. To switch off this check, use optim.dx.tol = +Inf
- lavInspect() gains new options: “vcov.def.joint” (and standardized variants) to give the (joint) variance-covariance matrix of both the free and `defined’ (using the := operator) variables; “loglik.casewise” to extract casewise (log)likelihood values (only if ML has been used); “h1.information” to extract the information matrix of the unrestricted h1 model
- lavTestScore() + uni table and modindices() output (in particular epc and epv values) are now more alike
- lavPredict() + Bartlett now uses FIML to compute factor scores (in the presence of missing data); new append.data= argument cbinds factor scores and raw data; if assemble = TRUE, factor scores of multiple groups will be joined in a single data.frame (with a group column); accept newdata= argument, even if sample statistics were used when fitting the original model
- using ordered = TRUE implies all variables should be treated as ordinal
- new logical arguments orthogonal.y and orthogonal.x can be used to specify zero covariances between endogenous and exogenous latent variables (only) respectively
- new option sampling.weights.normalization (set to “total” by default)
- group.equal now also accepts “composite.loadings” when the “<~” operator is used
- parameterEstimates() gains a remove.nonfree= argument
- initial (but still experimental and undocumented) support for rotation, EFA, and ESEM (contact me if you wish to test this)
- initial (but still experimental and undocumented) support for the `Structural After Measurement’ (SAM) approach (see the new lavaan:::sam() function; contact me if you wish to test this)

- Known issues:
- same as 0.6-1 (although many long standing github issues have been resolved)

- Bugs/glitches discovered after the release:
- twolevel models: standard errors are off for fixed effects (only) if the model contains a regression of the form y ~ x, both variables are observed, and x is not centered
- twolevel models: the chi-square test statistic for the baseline model is wrong (too low): the chi-square test statistic for the full model was substracted; therefore, if the user model fitted really bad, the baseline test statistic was too low; if the user model fitted reasonably well, there was (almost) no impact on the CFI/TLI fit measures
- lavPredict(): labels were wrong in the multigroup twolevel case

- Released on CRAN: 22 September 2018
- New features and user-visible changes:
- new function lavResiduals() provides raw and standardized residuals in addition to summary statistics (rmr, srmr and crmr, including their standard error, a test for exact fit, and an unbiased version); for continuous data only (single-level, conditional.x = FALSE)
- new option missing = “ml.x” or missing = “fiml.x” will not delete cases with missing values on exogenous covariates, even if fixed.x = TRUE (this restores the behavior of < 0.6); this can be useful for models with a large number of exogenous covariates, which you can treat as stochastic, but where fixed.x = TRUE is just more convenient
- lavInspect/lavTech + “sampstat” now always returns the data summaries from the h1 slot; this implies that if data is missing, and missing = “(fi)ml)”, you get the ‘EM’ estimated covariance matrix and mean vector (instead of the pairwise deleted covariance matrix from the samplestats slot)
- lavInspect/lavTech + “sampstat” and “implied” no longer return the mean vector if meanstructure = FALSE
- srmr_bollen is renamed crmr in the output of fitMeasures(), and does no longer count the (zero) diagonal elements when taking the average
- if conditional.x = TRUE, the baseline model will now set the slopes free (instead of fixing them to zero); this will usually result in a much better fit for the baseline model, and a less optimistic value for CFI/TLI and friends; to get back the old behaviour, set the option ‘baseline.conditional.x.free.slopes’ to FALSE

- Known issues:
- same as 0.6-1

- Bugs/glitches discovered after the release:
- mimic = “Mplus” was no longer setting meanstructure = TRUE
- if a partable was provided as input (instead of model syntax), the `y’ variables were identified by using lavNames(fit, “ov.y”) instead of lavNames(fit, “ov.nox”); therefore, we ‘missed’ some variables in lav_dataframe_check_ordered(), not (automatically) treating them as ordered
- mimic = “EQS” was not setting all the correct settings any longer
- lavPredict() + Bartlett + missing data did not produce ‘true’ FIML factor scores (although they were very close)
- lav_partable_merge() did not set the group/level column right, affecting lavTestLRT() when method = “SatorraBentler2010” (producing a warning that the optimizer could not find a solution, and no test statistic)
- when a second-order factor was included in the model, the summary output did not print a dot (‘.’, to indicate endogenous) in front of the (residual) variances of the first-order latent variables
- lavTestLRT() + Satorra2000 with two models having the same df resulted in a strange error
- lavTestScore() + uni resulted in epc values with the wrong sign; as a result, the epv values were in the wrong direction
- lavaanList() produced “nlev too low for some vars” error, when ordered variables were involved that included missing values (NA) (the NA was counted as an additional category)

- Released on CRAN: 16 July 2018
- New features and user-visible changes:
- cluster-robust standard errors are reported when the cluster= argument is used in combination with a standard single-level syntax (without the “level:” keywords); this can be used in combination with missing = “fiml” (to handle missing values) and sampling weights
- added lower() and upper() modifiers in the model syntax; it works in a similar way as the start() modifier to provide starting values; for example, to force a variance (say, y1 ~~ y1) to be larger than 0.001, you can now write y ~~ lower(0.001)*y1
- even if the optimizer claims to have found a solution, we check if the gradient is (almost) zero, and warn if not; this check can be turned on/off by the option check.gradient = TRUE/FALSE
- more effort is done to check if the model is identified in the presence of equality constraints; a warning is given if vcov is (nearly) singular; this check can be turned on/off by the option check.vcov = TRUE/FALSE
- header of the summary() function now shows the optimizer, the number of free parameters, and the number of constraints (if any)
- fabin3 starting values for factor loadings are now also computed if std.lv = TRUE

- Known issues:
- same as 0.6-1

- Bugs/glitches discovered after the release:
- ifi.scaled fit measure was not scaled
- lavTables() did not work if the first argument was a data.frame
- two-level + optim.method = “em” resulted in an error (about the check argument)
- modindices() did not work if conditional.x = TRUE
- lavInspect(, “data”) did not return covariates if conditional.x = TRUE

- Released on CRAN: 22 May 2018
- New features and user-visible changes:
- initial support for two-level SEM with random intercepts; see example(Demo.twolevel)
- initial support for sampling weights (for non-clustered data only for now)
- the code to compute (robust) standard errors and (robust) test statistics has been rewritten; the code is now much more consistent, but this may result in small changes in the values of some robust test statistics as printed in the output of summary(); a meanstructure is no longer added automatically when switching to a robust estimator
- conditional.x = FALSE now works in the categorical case (both with estimators DWLS and PML)
- if fixed.x = TRUE, we compute the loglikelihood without the exogenous covariates, and we do not allow for missing values in the exogenous covariates when missing = “ml” (and fixed.x = TRUE)
- estimator = “PML” now works for continuous-only data, or mixed continuous/ordinal data
- lavTestLRT(, method = “satorra2000”) which is the default when comparing models that were fitted with estimator MLMV or WLSMV now uses the so-called ‘delta’ method by default; this allows for comparing models that are nested in the covariance sense (and not just in the parameter sense); in addition, a scaled.shifted test statistic will be used by default
- the warning about empty cells in bivariate cells (when categorical data is used) is no longer shown
- many, many small changes under the hood

- Known issues:
- two-level SEM: no random slopes, no missing data, no categorical data
- the marginal ML infrastructure (estimator = “MML”) has not been updated yet
- several github issues have not been resolved yet

- Bugs/glitches discovered after the release:
- robust CFI/TLI and robust RMSEA values are printed as NA when estimator = “MLR”
- lavPredict(, level = 2) does not handle phantom latent variables properly