moeru-ai/airi · warning

property key 'version' wasn't found in the value of key ${ke

Error message

property key 'version' wasn't found in the value of key ${key} as ${value}, will keep the current ${toValue(initialValue)}

What it means

The same composable requires stored values to carry a top-level 'version' property. When the parsed JSON for the key has no version field, it cannot be version-checked, so the value is treated as unrecognizable: storage and data are reset to { version: defaultVersion, data: initialValue } with this warning naming the offending raw value.

Source

Thrown at packages/stage-shared/src/composables/use-versioned-local-storage/index.ts:67

              syncDataToStorage.resume()
            }
          }
          else {
            console.warn(`version ${rawValue.value.version} doesn't satisfy the version ${defaultVersion} for key ${key}, will reset the value to default value ${toValue(initialValue)}`)
            rawValue.value = { version: defaultVersion, data: toValue(initialValue) }
            syncDataToStorage.pause()
            data.value = toValue(initialValue)
            syncDataToStorage.resume()
          }
        }

        syncDataToStorage.pause()
        data.value = rawValue.value.data!
        syncDataToStorage.resume()
        return
      }

      console.warn(`property key 'version' wasn't found in the value of key ${key} as ${value}, will keep the current ${toValue(initialValue)}`)
      rawValue.value = { version: defaultVersion, data: toValue(initialValue) }
      syncDataToStorage.pause()
      data.value = toValue(initialValue)
      syncDataToStorage.resume()
    }
    catch (err) {
      console.warn(`failed to un-marshal Local Storage value, possibly due to incompatible or corrupted for key ${key} value ${value}, falling back to default value ${toValue(initialValue)}`, err)
      rawValue.value = { version: defaultVersion, data: toValue(initialValue) }
      syncDataToStorage.pause()
      data.value = toValue(initialValue)
      syncDataToStorage.resume()
    }
  }, {
    immediate: true,
    deep: true,
  })

  return data

View on GitHub (pinned to 677329427f)

Solutions

  1. Use a new (or newly prefixed) storage key when introducing versioning, so legacy plain values are never read
  2. Pre-migrate on first run: read the raw value yourself, and if it lacks a version, wrap it into the envelope before the composable initializes
  3. Keep localStorage keys unique per feature to avoid cross-writers

Example fix

// before
const { data } = useVersionedLocalStorage('settings', defaults, { version: 1 })
// legacy key holds { theme: 'dark' } without version -> resets to defaults

// after
const legacy = localStorage.getItem('settings')
if (legacy && !('version' in JSON.parse(legacy))) {
  localStorage.setItem('settings', JSON.stringify({ version: 1, data: JSON.parse(legacy) }))
}
const { data } = useVersionedLocalStorage('settings', defaults, { version: 1 })
Defensive patterns

Strategy: validation

Validate before calling

const raw = localStorage.getItem(key)
if (raw) {
  const parsed = JSON.parse(raw)
  if (!('version' in parsed)) {
    // legacy value: wrap it into the envelope before the composable reads it
    localStorage.setItem(key, JSON.stringify({ version: 1, data: parsed }))
  }
}

Type guard

function hasVersionField(value: unknown): value is { version: unknown } {
  return typeof value === 'object' && value !== null && 'version' in value
}

Prevention

When it happens

Trigger: A localStorage key written by an older unversioned implementation, another feature using the same key with plain JSON, or hand-edited/devtools-modified storage lacking the envelope.

Common situations: Introducing use-versioned-local-storage on a key that previously stored a plain object; key collisions between features; stale values from a pre-release build.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/12c6476f2defdd31. Report an issue: GitHub.