{"record":{"id":"b7e9d24da9b0d9b1","repo":"moeru-ai/airi","slug":"version-rawvalue-value-version-doesn-t-satisfy","errorCode":null,"errorMessage":"version ${rawValue.value.version} doesn't satisfy the version ${defaultVersion} for key ${key}, will reset the value to default value ${toValue(initialValue)}","messagePattern":"version (.+?) doesn't satisfy the version (.+?) for key (.+?), will reset the value to default value (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/stage-shared/src/composables/use-versioned-local-storage/index.ts","lineNumber":53,"sourceCode":"  }, {\n    deep: true,\n  })\n\n  watch(rawValue, (value) => {\n    try {\n      if ('version' in rawValue.value && rawValue.value.version != null) {\n        if (options?.satisfiesVersionBy != null && !options.satisfiesVersionBy(rawValue.value.version, defaultVersion)) {\n          if (options.onVersionMismatch != null) {\n            const action = options.onVersionMismatch(rawValue.value)\n            if (action.action === 'reset') {\n              rawValue.value = { version: defaultVersion, data: toValue(initialValue) }\n              syncDataToStorage.pause()\n              data.value = toValue(initialValue)\n              syncDataToStorage.resume()\n            }\n          }\n          else {\n            console.warn(`version ${rawValue.value.version} doesn't satisfy the version ${defaultVersion} for key ${key}, will reset the value to default value ${toValue(initialValue)}`)\n            rawValue.value = { version: defaultVersion, data: toValue(initialValue) }\n            syncDataToStorage.pause()\n            data.value = toValue(initialValue)\n            syncDataToStorage.resume()\n          }\n        }\n\n        syncDataToStorage.pause()\n        data.value = rawValue.value.data!\n        syncDataToStorage.resume()\n        return\n      }\n\n      console.warn(`property key 'version' wasn't found in the value of key ${key} as ${value}, will keep the current ${toValue(initialValue)}`)\n      rawValue.value = { version: defaultVersion, data: toValue(initialValue) }\n      syncDataToStorage.pause()\n      data.value = toValue(initialValue)\n      syncDataToStorage.resume()","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-shared/src/composables/use-versioned-local-storage/index.ts#L35-L71","documentation":"use-versioned-local-storage persists { version, data } envelopes in localStorage. On read, if the stored version fails the satisfiesVersionBy check against the current defaultVersion and no onVersionMismatch handler is configured, the stored data is discarded: both the storage value and the reactive data reset to initialValue, and this warning records the discarded version and the reset value.","triggerScenarios":"Bumping defaultVersion (or tightening satisfiesVersionBy) after users already have older-version data in the key; writing a satisfiesVersionBy predicate that rejects versions you actually consider compatible (e.g. exact equality instead of a range).","commonSituations":"Shipping a settings-schema change without a migration step; versioning a composable for the first time on a key that already had versioned data from an earlier experiment; predicate bugs that use !== against semver strings.","solutions":["Provide an onVersionMismatch handler that migrates rawValue.data to the new shape (return { action: 'migrate', value }) instead of accepting the reset","Fix satisfiesVersionBy to accept every still-compatible version, not just the default","Bump defaultVersion whenever the stored data shape changes so old envelopes are detected at all"],"exampleFix":"// before\nuseVersionedLocalStorage('my-key', defaultValue, {\n  version: 2,\n  // no satisfies/onMismatch -> v1 users silently reset to default\n})\n\n// after\nuseVersionedLocalStorage('my-key', defaultValue, {\n  version: 2,\n  satisfiesVersionBy: v => v >= 1,\n  onVersionMismatch: (raw) => {\n    const migrated = migrateFromV1(raw.data)\n    return { action: 'migrate', value: migrated }\n  },\n})","handlingStrategy":"fallback","validationCode":"const stored = JSON.parse(localStorage.getItem(key) ?? 'null')\nif (stored && satisfiesVersionBy(stored.version, targetVersion)) {\n  // safe: current code understands the stored version\n}","typeGuard":"function isVersionedEnvelope(value: unknown): value is { version: number, data: unknown } {\n  return typeof value === 'object' && value !== null\n    && typeof (value as { version?: unknown }).version === 'number'\n    && 'data' in value\n}","tryCatchPattern":null,"preventionTips":["Always pass onVersionMismatch with a real migration when the data shape changed","Write satisfiesVersionBy as a compatibility range, not exact equality","Bump the version the moment the persisted shape changes, and cover the migration with a test"],"tags":["local-storage","versioning","data-migration","composable"],"backgroundTag":"stored-data-version-mismatch","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}