Meituan-Dianping/mpvue · warning

Avoid mutating an injected value directly since the changes

Error message

Avoid mutating an injected value directly since the changes will be overwritten whenever the provided component re-renders. injection being mutated: "${key}"

What it means

When a component `inject`s a reactive value from an ancestor's `provide`, Vue makes the injected property reactive with a custom setter that warns on direct mutation. Because the provider re-renders and re-provides the value, any local mutation would be silently overwritten, so Vue forbids it.

Source

Thrown at packages/weex-vue-framework/factory.js:3364

function initProvide (vm) {
  var provide = vm.$options.provide;
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide;
  }
}

function initInjections (vm) {
  var result = resolveInject(vm.$options.inject, vm);
  if (result) {
    observerState.shouldConvert = false;
    Object.keys(result).forEach(function (key) {
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        defineReactive$$1(vm, key, result[key], function () {
          warn(
            "Avoid mutating an injected value directly since the changes will be " +
            "overwritten whenever the provided component re-renders. " +
            "injection being mutated: \"" + key + "\"",
            vm
          );
        });
      } else {
        defineReactive$$1(vm, key, result[key]);
      }
    });
    observerState.shouldConvert = true;
  }
}

function resolveInject (inject, vm) {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    var result = Object.create(null);

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Stop mutating the injected value; treat it as read-only coming from the provider.
  2. Copy the injected value into local `data` if the child needs its own editable copy.
  3. Move the shared state into a proper store (Vuex) or have children emit events / call methods on the provider to change it.
  4. If you only need a non-reactive constant, provide a frozen/plain object so no reactive setter is installed.

Example fix

// before
this.theme = 'dark'; // injected
// after
data() { return { localTheme: this.theme } }
Defensive patterns

Strategy: type-guard

Validate before calling

function canInject(key, vm) {
  let s = vm.$parent;
  while (s) { if (s._provided && key in s._provided) return true; s = s.$parent; }
  return false;
}

Type guard

function isInjectedReadonly(vm, key) {
  const desc = Object.getOwnPropertyDescriptor(vm, key);
  return !!desc && typeof desc.set === 'function';
}

Prevention

When it happens

Trigger: `this.injectedKey = newValue` (or `Vue.set`/assignment via the reactive setter) on a property declared in the `inject` option or returned from a functional `provide` whose value comes from `inject`, in a development build.

Common situations: Using inject/inject like a global store and writing to it from a child; form components mutating injected config; migration of code that previously used `$parent` access directly.

Related errors


AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02). Data as JSON: /api/errors/9d4d52a64df42229. Report an issue: GitHub.