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
- Stop mutating the injected value; treat it as read-only coming from the provider.
- Copy the injected value into local `data` if the child needs its own editable copy.
- Move the shared state into a proper store (Vuex) or have children emit events / call methods on the provider to change it.
- 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
- Never write to injected values; snapshot into data if editing is needed.
- Provide defaults via the inject option object syntax.
- Use Vuex/composition for genuinely writable shared state.
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
- Injection "${key}" not found
- $props is readonly.
- Avoid using observed data object as vnode data: ${JSON.strin
- Avoid mutating a prop directly since the value will be overw
- Avoid replacing instance root $data. Use nested data propert
AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02).
Data as JSON: /api/errors/9d4d52a64df42229.
Report an issue: GitHub.