{"record":{"id":"690d78c4d54f2041","repo":"janhq/jan","slug":"invalid-modelid-modelid-only-alphanumeric-and-690d78","errorCode":null,"errorMessage":"Invalid modelId: ${modelId}. Only alphanumeric and / _ - . characters are allowed.","messagePattern":"Invalid modelId: (.+?)\\. Only alphanumeric and / _ - \\. characters are allowed\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/mlx-extension/src/index.ts","lineNumber":577,"sourceCode":"    }\n    const newModelConfigPath = await joinPath([newFolderPath, 'model.yml'])\n    await fs.mv(modelFolderPath, newFolderPath).then(() =>\n      invoke('write_yaml', {\n        data: {\n          ...modelConfig,\n          model_path: modelConfig?.model_path?.replace(\n            `mlx/models/${modelId}`,\n            `mlx/models/${model.id}`\n          ),\n        },\n        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([","sourceCodeStart":559,"sourceCodeEnd":595,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/extensions/mlx-extension/src/index.ts#L559-L595","documentation":"Thrown by import() when modelId fails isValidModelId(). The validator requires the id to match ^[a-zA-Z0-9/_\\-\\.]+$ and have no empty/`.`/`..` path segments. This blocks path traversal and unsafe characters before the id is used to build a filesystem path. It is the first check in import(), before any disk access.","triggerScenarios":"Calling import() with a modelId containing spaces, colons, non-ASCII, or special shell characters; an id with '../' segments or a leading/trailing slash producing empty parts; an id built from untrusted user input without sanitization.","commonSituations":"Pasting a Hugging Face repo id with '@', spaces, or parentheses; constructing modelId from a filename with spaces; attempted path traversal via '..'; id derived from model metadata with disallowed punctuation.","solutions":["Sanitize modelId to the allowed charset (alphanumeric, /, _, -, .) before calling import().","Reject or strip path-traversal segments ('..') upstream in the UI.","Map disallowed characters (spaces -> '-', colons -> '-') deterministically.","Reuse isValidModelId() to validate before showing the import action to the user."],"exampleFix":"// before\nawait engine.import(rawName, { modelPath: url })\n\n// after\nfunction safeId(raw: string): string {\n  return raw.replace(/[^a-zA-Z0-9/_\\-.]/g, '-').replace(/\\/\\.\\.\\//g, '')\n}\nconst modelId = safeId(rawName)\nawait engine.import(modelId, { modelPath: url })","handlingStrategy":"validation","validationCode":"import { isValidModelId } from './mlx-extension' // exported helper\n\nfunction sanitizeModelId(raw: string): string {\n  return raw.replace(/[^a-zA-Z0-9/_\\-.]/g, '-').replace(/\\.\\./g, '')\n}\n\nconst modelId = sanitizeModelId(rawName)\nif (!isValidModelId(modelId)) throw new Error('Cannot sanitize model id')\nawait engine.import(modelId, opts)","typeGuard":"import { isValidModelId } from './mlx-extension'\n\nfunction isSafeModelId(id: string): id is string {\n  return isValidModelId(id)\n}","tryCatchPattern":null,"preventionTips":["Sanitize untrusted model names to the allowed charset before import.","Block '..' and empty path segments at the input layer.","Use isValidModelId() to gate the import button in the UI."],"tags":["validation","security","path-traversal","import","mlx","typescript"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}