{"record":{"id":"f2235e03a624bdfb","repo":"janhq/jan","slug":"model-modelid-already-exists-f2235e","errorCode":null,"errorMessage":"Model ${modelId} already exists","messagePattern":"Model (.+?) already exists","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/mlx-extension/src/index.ts","lineNumber":588,"sourceCode":"        savePath: newModelConfigPath,\n      })\n    )\n  }\n\n  override async import(modelId: string, opts: ImportOptions): Promise<void> {\n    if (!isValidModelId(modelId))\n      throw new Error(\n        `Invalid modelId: ${modelId}. Only alphanumeric and / _ - . characters are allowed.`\n      )\n\n    const configPath = await joinPath([\n      await this.getProviderPath(),\n      'models',\n      modelId,\n      'model.yml',\n    ])\n    if (await fs.existsSync(configPath))\n      throw new Error(`Model ${modelId} already exists`)\n\n    const sourcePath = opts.modelPath\n\n    if (sourcePath.startsWith('https://')) {\n      // Download from URL to mlx models folder\n      const janDataFolderPath = await getJanDataFolderPath()\n      const modelDir = await joinPath([\n        janDataFolderPath,\n        'mlx',\n        'models',\n        modelId,\n      ])\n      const localPath = await joinPath([modelDir, 'model.safetensors'])\n\n      const downloadManager = window.core.extensionManager.getByName(\n        '@janhq/download-extension'\n      )\n","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/extensions/mlx-extension/src/index.ts#L570-L606","documentation":"Thrown by MlxExtension.import() when a model.yml already exists at <providerPath>/models/<modelId>/model.yml. The extension refuses to clobber an existing model registration, so import is treated as create-only, not upsert. The check runs after modelId format validation but before any download or folder copy.","triggerScenarios":"Calling import(modelId, opts) a second time with the same modelId; calling import after a previous import partially succeeded and left model.yml behind; importing a modelId that collides with a model registered through a different provider path.","commonSituations":"Re-running an import after a download error was already recovered; user re-clicking 'Import' in the UI for the same model; CI re-running a setup script that imports MLX models; modelId collision between a HuggingFace-style ID and a local alias.","solutions":["Delete or rename the existing folder <JanData>/mlx/models/<modelId> (and its model.yml) before re-importing.","Use a different modelId for the new import to avoid the collision.","Call a list/get API (or fs.existsSync on the config path) first and treat an existing model as an update, not an error.","If the previous import failed mid-way, clean up the partial model.yml it left behind."],"exampleFix":"// before\nawait mlx.import(modelId, opts) // throws if model.yml exists\n\n// after\nconst exists = await fs.existsSync(configPath)\nif (exists) {\n  await fs.rm(modelDir, { recursive: true, force: true })\n}\nawait mlx.import(modelId, opts)","handlingStrategy":"validation","validationCode":"import { fs, joinPath } from '@janhq/janope'\nasync function modelExists(modelId: string): Promise<boolean> {\n  const providerPath = await mlx.getProviderPath()\n  const configPath = await joinPath([providerPath, 'models', modelId, 'model.yml'])\n  return fs.existsSync(configPath)\n}\nif (await modelExists(modelId)) {\n  // delete first, or pick a new id, or treat as update\n}","typeGuard":null,"tryCatchPattern":"try {\n  await mlx.import(modelId, opts)\n} catch (e) {\n  if (e instanceof Error && e.message.endsWith('already exists')) {\n    // idempotent: model already registered\n  } else throw e\n}","preventionTips":["Track which modelIds have been imported so re-imports are intentional.","Clean up the model folder before re-importing to avoid stale model.yml.","Use a unique modelId per source to avoid collisions."],"tags":["model-management","mlx","import","filesystem","duplicate"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}