{"record":{"id":"b96bea8728dc74b4","repo":"nexu-io/open-design","slug":"invalid-live-artifact-id","errorCode":null,"errorMessage":"invalid live artifact id","messagePattern":"invalid live artifact id","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/live-artifacts/store.ts","lineNumber":294,"sourceCode":"\nexport function generateLiveArtifactId(options: GenerateLiveArtifactIdOptions): string {\n  const randomSuffix = options.randomSuffix ?? randomBytes(LIVE_ARTIFACT_ID_RANDOM_BYTES).toString('hex');\n  if (!/^[a-f0-9]+$/i.test(randomSuffix) || randomSuffix.length === 0) {\n    throw new Error('invalid live artifact id random suffix');\n  }\n\n  const suffix = randomSuffix.toLowerCase();\n  const maxSlugLength = MAX_LIVE_ARTIFACT_STORAGE_ID_LENGTH - LIVE_ARTIFACT_ID_PREFIX.length - suffix.length - 2;\n  if (maxSlugLength < 1) {\n    throw new Error('invalid live artifact id random suffix');\n  }\n  const slug = truncateSlugAtSegmentBoundary(generateLiveArtifactSlug(options.slug ?? options.title), maxSlugLength);\n  return validateLiveArtifactStorageId(`${LIVE_ARTIFACT_ID_PREFIX}-${slug}-${suffix}`);\n}\n\nexport function validateLiveArtifactStorageId(artifactId: string): string {\n  if (!SAFE_LIVE_ARTIFACT_ID.test(artifactId) || artifactId === '.' || artifactId === '..') {\n    throw new Error('invalid live artifact id');\n  }\n  return artifactId;\n}\n\nexport function liveArtifactsRootDir(projectsRoot: string, projectId: string): string {\n  const projectDirPath = path.resolve(projectDir(projectsRoot, projectId));\n  return resolveInside(projectDirPath, LIVE_ARTIFACTS_DIR_NAME, 'live artifact path escapes project dir');\n}\n\nexport function liveArtifactStorePaths(\n  projectsRoot: string,\n  projectId: string,\n  artifactId: string,\n): LiveArtifactStorePaths {\n  const safeArtifactId = validateLiveArtifactStorageId(artifactId);\n  const projectDirPath = path.resolve(projectDir(projectsRoot, projectId));\n  const rootDir = liveArtifactsRootDir(projectsRoot, projectId);\n  const artifactDir = resolveInside(rootDir, safeArtifactId, 'live artifact path escapes storage root');","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/live-artifacts/store.ts#L276-L312","documentation":"Thrown by validateLiveArtifactStorageId() when an artifactId fails the safe-id regex `/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/` or equals '.'/'..'. Every storage path derives from this id (it becomes a directory name under .live-artifacts/), so an unsafe value could escape the project dir or collide with filesystem metadata. The function is the single gate called by liveArtifactStorePaths(), getLiveArtifact(), and friends.","triggerScenarios":"Calling getLiveArtifact/update with a hand-crafted id containing '/', spaces, unicode, or exceeding 128 chars; an id of literally '.' or '..'; an id starting with a non-alphanumeric character like '-foo' or '_bar'; passing a URL or full path instead of the bare id.","commonSituations":"External client constructs an artifactId from user input or a URL slug without sanitizing; filesystem directory was renamed externally to an invalid name and listLiveArtifacts then re-validates each entry; migration tooling invents its own id scheme.","solutions":["Use ids returned by generateLiveArtifactId() — they always match the safe pattern (la-<slug>-<hexsuffix>).","If you must accept external ids, validate with the same regex before calling store functions: `/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(id) && id !== '.' && id !== '..'`.","Rename any externally-corrupted storage directory back to a valid id, or delete it.","Do not pass paths, URLs, or slugs-with-spaces as artifactId; pass only the bare id."],"exampleFix":"// before\ngetLiveArtifact({ projectsRoot, projectId, artifactId: 'my artifact/2' });\n// after\ngetLiveArtifact({ projectsRoot, projectId, artifactId: 'la-my-artifact-9f2a1c8e0b7d' });","handlingStrategy":"validation","validationCode":"import { validateLiveArtifactStorageId } from './store';\n\nfunction safeArtifactId(id: string): string {\n  try {\n    return validateLiveArtifactStorageId(id);\n  } catch {\n    throw new Error(`Refusing unsafe artifact id: ${JSON.stringify(id)}`);\n  }\n}\n\n// or inline at the trust boundary\nconst SAFE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\nfunction isSafeArtifactId(id: string): boolean {\n  return SAFE.test(id) && id !== '.' && id !== '..';\n}","typeGuard":"function isLiveArtifactStorageId(value: unknown): value is string {\n  return typeof value === 'string'\n    && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value)\n    && value !== '.'\n    && value !== '..';\n}","tryCatchPattern":null,"preventionTips":["Only use ids minted by generateLiveArtifactId().","Validate externally-supplied ids at the API boundary before they reach the store.","Never pass paths, URLs, or user slugs as the artifactId."],"tags":["live-artifacts","storage","validation","filesystem-safety","path-escape"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}