{"record":{"id":"e3bc4c884dee743f","repo":"mastra-ai/mastra","slug":"expectedversion-is-only-valid-for-an-existing-node","errorCode":null,"errorMessage":"expectedVersion is only valid for an existing node.","messagePattern":"expectedVersion is only valid for an existing node\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/memory/src/processors/observational-memory/subconscious/knowledge-write-tools.ts","lineNumber":254,"sourceCode":"          kind?: string;\n          content: string;\n          scope?: KnowledgeScopeLevel;\n          expectedVersion?: number;\n        };\n        const trimmedName = value.name.trim();\n        const reservedName = trimmedName.toLowerCase();\n        const name = reservedName === 'capture-guidance' ? reservedName : trimmedName;\n        if (reservedName === 'capture-guidance' && value.content.length > MAX_GUIDANCE_LENGTH) {\n          throw new Error(`capture-guidance is limited to ${MAX_GUIDANCE_LENGTH} characters.`);\n        }\n        const store = await getStore(memory);\n        const scope = resolveWriteScope(options, value.scope);\n        const resolvedNode = await store.resolveNode({ name, scope });\n        const existing =\n          resolvedNode && knowledgeScopeKey(resolvedNode.scope) === knowledgeScopeKey(scope) ? resolvedNode : null;\n        if (!existing) {\n          if (value.expectedVersion !== undefined)\n            throw new Error('expectedVersion is only valid for an existing node.');\n          return store.createNode({\n            name,\n            kind: value.kind ?? 'document',\n            content: value.content,\n            scope,\n            resolutionScope: options.scope,\n          });\n        }\n        if (value.expectedVersion === undefined) throw new Error('Updating node content requires expectedVersion.');\n        return store.updateNode({\n          id: existing.id,\n          version: value.expectedVersion,\n          kind: value.kind,\n          content: value.content,\n          resolutionScope: options.scope,\n        });\n      },\n    }),","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/memory/src/processors/observational-memory/subconscious/knowledge-write-tools.ts#L236-L272","documentation":"The node upsert tool uses expectedVersion for optimistic concurrency on existing nodes. If the (name, scope) does not resolve to an existing node, the call becomes a create, and expectedVersion is meaningless there — the library rejects the combination instead of ignoring it. This protects callers from thinking they are updating a node that actually does not exist yet.","triggerScenarios":"Passing expectedVersion with a name that has no node in the target scope — e.g. a typo'd node name, wrong scope level, or a node that was deleted or merged so resolveNode no longer matches within the same scope key.","commonSituations":"First-time writes to a node assumed to exist; scope mismatch so resolveNode looks in a different scope than where the node lives; concurrent merge removed the node between planning and execution.","solutions":["Omit expectedVersion to create the node, or first create the node then update with expectedVersion","Verify the node exists with store.resolveNode({ name, scope }) and that the scope matches the node's scope key","Correct the node name or scope level so it resolves to the existing node"],"exampleFix":"// before\nawait tool.execute({ name: 'typo-name', content, expectedVersion: 2 });\n// after\nconst resolved = await store.resolveNode({ name: 'node-name', scope });\nif (resolved) await tool.execute({ name: 'node-name', content, expectedVersion: resolved.version });\nelse await tool.execute({ name: 'node-name', content });","handlingStrategy":"validation","validationCode":"const resolved = await store.resolveNode({ name, scope });\nif (!resolved && expectedVersion !== undefined) {\n  delete input.expectedVersion; // create path\n}","typeGuard":null,"tryCatchPattern":"try {\n  await tool.execute(input);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('expectedVersion is only valid for an existing node')) {\n    const { expectedVersion, ...createInput } = input;\n    await tool.execute(createInput); // retry as create\n  } else throw e;\n}","preventionTips":["Resolve the node first and branch create/update explicitly","Only pass expectedVersion when the node is known to exist","Match scope levels exactly so resolveNode finds the node"],"tags":["validation","memory","optimistic-concurrency"],"backgroundTag":"invalid-input-combination","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}