{"record":{"id":"b531bd91491038b3","repo":"vercel/ai","slug":"method-value-stream-is-locked-and-cannot-be-up","errorCode":null,"errorMessage":"${method}: Value stream is locked and cannot be updated.","messagePattern":"(.+?): Value stream is locked and cannot be updated\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/rsc/src/streamable-value/create-streamable-value.ts","lineNumber":140,"sourceCode":"};\n\nfunction createStreamableValueImpl<T = any, E = any>(initialValue?: T) {\n  let closed = false;\n  let locked = false;\n  let resolvable = createResolvablePromise<StreamableValue<T, E>>();\n\n  let currentValue = initialValue;\n  let currentError: E | undefined;\n  let currentPromise: typeof resolvable.promise | undefined =\n    resolvable.promise;\n  let currentPatchValue: StreamablePatch;\n\n  function assertStream(method: string) {\n    if (closed) {\n      throw new Error(method + ': Value stream is already closed.');\n    }\n    if (locked) {\n      throw new Error(\n        method + ': Value stream is locked and cannot be updated.',\n      );\n    }\n  }\n\n  let warningTimeout: NodeJS.Timeout | undefined;\n  function warnUnclosedStream() {\n    if (process.env.NODE_ENV === 'development') {\n      if (warningTimeout) {\n        clearTimeout(warningTimeout);\n      }\n      warningTimeout = setTimeout(() => {\n        console.warn(\n          'The streamable value has been slow to update. This may be a bug or a performance issue or you forgot to call `.done()`.',\n        );\n      }, HANGING_STREAM_WARNING_TIME_MS);\n    }\n  }","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/rsc/src/streamable-value/create-streamable-value.ts#L122-L158","documentation":"assertStream throws this when a mutating method (update, append, error, done) is called on a locked streamable value. Locking prevents any further updates while still allowing clients to keep reading the value (locking is distinct from closing).","triggerScenarios":"Calling .update()/.append()/.error()/.done() after .lock() was invoked — typically two code paths both owning the same streamable value where one locks it.","commonSituations":"Multiple server components/actions sharing one streamable value; one path locks for final rendering while another (e.g. a background job or a redirect) still tries to update.","solutions":["Ensure only one code path owns the streamable value and its lock","Call lock() only after all updates and appends are finished","Use separate streamable values for independent producers","Route further output through a different stream instead of the locked one"],"exampleFix":"// before\nstreamable.update('partial');\nstreamable.lock();\nstreamable.update('final'); // throws\n// after\nstreamable.update('partial');\nstreamable.update('final');\nstreamable.lock();","handlingStrategy":"validation","validationCode":"let isLocked = false;\nfunction safeUpdate(streamable, value) {\n  if (isLocked) throw new Error('Refusing update: stream locked');\n  streamable.update(value);\n}\n// set isLocked = true wherever you call streamable.lock()","typeGuard":"function canUpdate(streamable) {\n  return streamable && !streamable.locked; // track your own lock flag if not exposed\n}","tryCatchPattern":"try {\n  streamable.update(value);\n} catch (e) {\n  if (!/Value stream is locked/.test(String(e.message))) throw e;\n  // else: route output elsewhere or log\n}","preventionTips":["Ensure a single owner calls lock(), and only after all updates/appends complete","Document lock/close ownership when sharing streamable values across modules","Prefer separate streamable values for independent producers"],"tags":["streaming","rsc","lifecycle","locked-stream"],"backgroundTag":"stream-locked","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}