{"record":{"id":"bfe07d038460c074","repo":"nexu-io/open-design","slug":"registerlibraryasset-requires-bytes-text-or-absp","errorCode":null,"errorMessage":"registerLibraryAsset requires bytes, text, or absPath","messagePattern":"registerLibraryAsset requires bytes, text, or absPath","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/daemon/src/library.ts","lineNumber":339,"sourceCode":" * are written content-addressed under LIBRARY_DIR; referenced assets only\n * store a pointer. Always appends a source record for the back-link graph.\n */\nexport async function registerLibraryAsset(\n  input: RegisterLibraryAssetInput,\n): Promise<RegisterLibraryAssetResult> {\n  const { db, libraryDir } = input;\n\n  let bytes = input.bytes ?? null;\n  let mime = input.mime;\n  if (!bytes && typeof input.text === 'string') {\n    bytes = Buffer.from(input.text, 'utf8');\n    if (!mime) mime = 'text/plain';\n  }\n  if (!bytes && input.absPath) {\n    bytes = await readFile(input.absPath);\n  }\n  if (!bytes) {\n    throw new Error('registerLibraryAsset requires bytes, text, or absPath');\n  }\n\n  const contentHash = createHash('sha256').update(bytes).digest('hex');\n\n  // Dedup: same bytes already indexed → append source, union tags.\n  const existing = findLibraryAssetByHash(db, contentHash);\n  if (existing) {\n    return dedupIntoExistingAsset(db, existing, input);\n  }\n\n  if (!mime) mime = detectMime(bytes, input.filename);\n  const kind = input.kind ?? kindForMime(mime);\n  const dims = kind === 'image' ? sniffImageDimensions(bytes) : null;\n  const now = Date.now();\n  // The artifact's own time (DS update / file mtime) when the caller supplies\n  // it, so the timeline buckets by creation rather than sync time; else now.\n  const capturedAt = Number.isFinite(input.capturedAt) ? Number(input.capturedAt) : now;\n  const id = randomUUID();","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/library.ts#L321-L357","documentation":"Raised by registerLibraryAsset() when none of input.bytes, input.text, or input.absPath produced a Buffer. The function accepts three payload shapes (raw bytes, utf-8 text, or a file to read) and treats the absence of all three as a caller contract violation rather than silently registering an empty asset.","triggerScenarios":"A caller builds RegisterLibraryAssetInput with source/metadata but forgets to attach bytes/text/absPath; absPath was set but the file did not exist (readFile would throw earlier) or absPath was an empty string (falsy, so the branch was skipped).","commonSituations":"Refactor that stopped passing bytes; conditional payload building that leaves all three undefined for an edge case; caller assumes mime/filename implies content; absPath set to '' after a bad path sanitization step.","solutions":["Before calling, ensure exactly one of bytes / text / absPath is set (and absPath is a non-empty existing file).","Validate the input at the call site and throw a more specific error pointing at the caller.","If the caller only has a URL, download to bytes first, then pass bytes."],"exampleFix":"// before\nawait registerLibraryAsset({ db, libraryDir, storage, source, mime, filename });\n\n// after\nawait registerLibraryAsset({\n  db, libraryDir, storage, source,\n  bytes: await readFile(localFilePath),\n  mime, filename,\n});","handlingStrategy":"validation","validationCode":"import type { RegisterLibraryAssetInput } from '../library.js';\n\nfunction hasRegisterablePayload(input: RegisterLibraryAssetInput): boolean {\n  return Boolean(input.bytes || (typeof input.text === 'string' && input.text) || input.absPath);\n}\n\nif (!hasRegisterablePayload(input)) {\n  throw new Error('caller: registerLibraryAsset needs one of bytes/text/absPath');\n}","typeGuard":"function isRegisterableInput(input: RegisterLibraryAssetInput): input is RegisterLibraryAssetInput & { bytes: Buffer } {\n  return Boolean(input.bytes) || typeof input.text === 'string' || Boolean(input.absPath);\n}","tryCatchPattern":"try {\n  return await registerLibraryAsset(input);\n} catch (err) {\n  if (err instanceof Error && err.message === 'registerLibraryAsset requires bytes, text, or absPath') {\n    // caller bug — fix the call site, do not retry\n    throw new Error('caller did not provide asset payload');\n  }\n  throw err;\n}","preventionTips":["Always attach exactly one of bytes/text/absPath at the call site.","If absPath is computed, ensure it is a non-empty existing file before passing.","Treat this as a programmer error, not a runtime/retry condition."],"tags":["validation","library","api-contract"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}