Repository: caddyserver/caddy
File: context.go (Go)

Code before:

	if val == nil {
		// returned module values are almost always type-asserted
		// before being used, so a nil value would panic; and there
		// is no good reason to explicitly declare null modules in
		// a config; it might be because the user is trying to achieve
		// a result the developer isn't expecting, which is a smell
		return nil, fmt.Errorf("module value cannot be null")
	}

	var err error

	// if this is an app module, keep a reference to it,
	// since submodules may need to reference it during
	// provisioning (even though the parent app module
	// may not be fully provisioned yet; this is the case
	// with the tls app's automation policies, which may
	// refer to the tls app to check if a global DNS
	// module has been configured for DNS challenges)
	if appModule, ok := val.(App); ok {
		ctx.cfg.apps[id] = appModule
		defer func() {
			if err != nil {
				delete(ctx.cfg.apps, id)
			}
		}()
	}

	ctx.ancestry = append(ctx.ancestry, val)

	if prov, ok := val.(Provisioner); ok {
		err = prov.Provision(ctx)
		if err != nil {
			// incomplete provisioning could have left state
			// dangling, so make sure it gets cleaned up
			if cleanerUpper, ok := val.(CleanerUpper); ok {
				err2 := cleanerUpper.Cleanup()
				if err2 != nil {
					err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
				}
			}
			return nil, fmt.Errorf("provision %s: %v", modInfo, err)
		}
	}

	if validator, ok := val.(Validator); ok {
		err = validator.Validate()
		if err != nil {
			// since the module was already provisioned, make sure we clean up
			if cleanerUpper, ok := val.(CleanerUpper); ok {
				err2 := cleanerUpper.Cleanup()

Review comment (bug):
As said in this [comment](https://github.com/caddyserver/caddy/pull/7070#issuecomment-3126028802), recording the error of an app that fails to provision or validate will only initialize the app once and reduce the work caddy needs to do.

Code after:

	if val == nil {
		// returned module values are almost always type-asserted
		// before being used, so a nil value would panic; and there
		// is no good reason to explicitly declare null modules in
		// a config; it might be because the user is trying to achieve
		// a result the developer isn't expecting, which is a smell
		return nil, fmt.Errorf("module value cannot be null")
	}

	var err error

	// if this is an app module, keep a reference to it,
	// since submodules may need to reference it during
	// provisioning (even though the parent app module
	// may not be fully provisioned yet; this is the case
	// with the tls app's automation policies, which may
	// refer to the tls app to check if a global DNS
	// module has been configured for DNS challenges)
	if appModule, ok := val.(App); ok {
		ctx.cfg.apps[id] = appModule
		defer func() {
			if err != nil {
				ctx.cfg.failedApps[id] = err
			}
		}()
	}

	ctx.ancestry = append(ctx.ancestry, val)

	if prov, ok := val.(Provisioner); ok {
		err = prov.Provision(ctx)
		if err != nil {
			// incomplete provisioning could have left state
			// dangling, so make sure it gets cleaned up
			if cleanerUpper, ok := val.(CleanerUpper); ok {
				err2 := cleanerUpper.Cleanup()
				if err2 != nil {
					err = fmt.Errorf("%v; additionally, cleanup: %v", err, err2)
				}
			}
			return nil, fmt.Errorf("provision %s: %v", modInfo, err)
		}
	}

	if validator, ok := val.(Validator); ok {
		err = validator.Validate()
		if err != nil {
			// since the module was already provisioned, make sure we clean up
			if cleanerUpper, ok := val.(CleanerUpper); ok {
				err2 := cleanerUpper.Cleanup()