{"record":{"id":"a9803aa5e613cbd7","repo":"janhq/jan","slug":"invalid-backend-string-format-targetbackendstr","errorCode":null,"errorMessage":"Invalid backend string format: \"${targetBackendString}\". Expected \"version/backend\".","messagePattern":"Invalid backend string format: \"(.+?)\"\\. Expected \"version/backend\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/llamacpp-extension/src/index.ts","lineNumber":1885,"sourceCode":"    this.isUpdatingBackend = true\n    const startedAt = Date.now()\n    const from = this.config.version_backend\n    let previous: BackendSelection | undefined\n\n    try {\n      if (!targetBackendString)\n        throw new Error(\n          `Invalid backend string: ${targetBackendString} supplied to update function`\n        )\n\n      const backendParts = targetBackendString.split('/')\n\n      if (\n        backendParts.length !== 2 ||\n        !backendParts[0]?.trim() ||\n        !backendParts[1]?.trim()\n      ) {\n        throw new Error(\n          `Invalid backend string format: \"${targetBackendString}\". Expected \"version/backend\".`\n        )\n      }\n\n      const [rawVersion, rawBackend] = backendParts\n      const version = rawVersion.trim()\n      const backend = rawBackend.trim()\n\n      // Normalize the target backend string to use trimmed values\n      targetBackendString = `${version}/${backend}`\n\n      logger.info(\n        `Updating backend to ${targetBackendString} (backend type: ${backend})`\n      )\n\n      previous = await this.captureBackendSelection()\n\n      // Download new backend using the original asset/backend name","sourceCodeStart":1867,"sourceCodeEnd":1903,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/extensions/llamacpp-extension/src/index.ts#L1867-L1903","documentation":"Format validation in runUpdate: after confirming the string is non-empty, it is split on '/' and checked for exactly two non-empty, non-whitespace parts. A string that doesn't match 'version/backend' (e.g. 'v1', 'v1/', '/backend', 'a/b/c') throws.","triggerScenarios":"targetBackendString has zero, one, or more than one '/'; either the version or backend segment is empty or whitespace-only after trim. Examples: 'b1234', 'b1234/', '/cuda-12.4', 'v1/cuda/extra'.","commonSituations":"Backend string assembled from separate fields with one left blank; a stored preference string that was partially overwritten; user-typed or API-supplied value that doesn't follow the convention.","solutions":["Construct the backend string as `${version}/${backend}` from validated non-empty components.","Add a regex pre-check (e.g. /^\\S+\\/\\S+$/) before calling runUpdate.","Inspect the stored backend preference format and migrate legacy values to 'version/backend'."],"exampleFix":"// before\nthis.runUpdate(`${version}/${backend}`)  // backend might be ''\n// after\nif (!/^\\S+\\/\\S+$/.test(`${version}/${backend}`)) throw new Error('Bad format')\nthis.runUpdate(`${version}/${backend}`)","handlingStrategy":"validation","validationCode":"const BACKEND_STRING_RE = /^\\S+\\/\\S+$/\n\nfunction isValidBackendFormat(s: string): boolean {\n  return BACKEND_STRING_RE.test(s)\n}\n\n// Before calling runUpdate:\nif (!isValidBackendFormat(targetBackendString)) {\n  throw new Error(`Expected 'version/backend', got '${targetBackendString}'`)\n}","typeGuard":"function isVersionBackendString(s: unknown): s is string {\n  return typeof s === 'string' && /^\\S+\\/\\S+$/.test(s)\n}","tryCatchPattern":null,"preventionTips":["Always construct backend strings via template literals from validated components: `${version}/${backend}`.","Centralize backend-string creation in a helper to avoid ad-hoc concatenation."],"tags":["backend","validation","format","update","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}