janhq/jan · error · Error

Invalid modelId: ${modelId}. Only alphanumeric and / _ - . c

Error message

Invalid modelId: ${modelId}. Only alphanumeric and / _ - . characters are allowed.

What it means

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.

Source

Thrown at extensions/mlx-extension/src/index.ts:577

    }
    const newModelConfigPath = await joinPath([newFolderPath, 'model.yml'])
    await fs.mv(modelFolderPath, newFolderPath).then(() =>
      invoke('write_yaml', {
        data: {
          ...modelConfig,
          model_path: modelConfig?.model_path?.replace(
            `mlx/models/${modelId}`,
            `mlx/models/${model.id}`
          ),
        },
        savePath: newModelConfigPath,
      })
    )
  }

  override async import(modelId: string, opts: ImportOptions): Promise<void> {
    if (!isValidModelId(modelId))
      throw new Error(
        `Invalid modelId: ${modelId}. Only alphanumeric and / _ - . characters are allowed.`
      )

    const configPath = await joinPath([
      await this.getProviderPath(),
      'models',
      modelId,
      'model.yml',
    ])
    if (await fs.existsSync(configPath))
      throw new Error(`Model ${modelId} already exists`)

    const sourcePath = opts.modelPath

    if (sourcePath.startsWith('https://')) {
      // Download from URL to mlx models folder
      const janDataFolderPath = await getJanDataFolderPath()
      const modelDir = await joinPath([

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Sanitize modelId to the allowed charset (alphanumeric, /, _, -, .) before calling import().
  2. Reject or strip path-traversal segments ('..') upstream in the UI.
  3. Map disallowed characters (spaces -> '-', colons -> '-') deterministically.
  4. Reuse isValidModelId() to validate before showing the import action to the user.

Example fix

// before
await engine.import(rawName, { modelPath: url })

// after
function safeId(raw: string): string {
  return raw.replace(/[^a-zA-Z0-9/_\-.]/g, '-').replace(/\/\.\.\//g, '')
}
const modelId = safeId(rawName)
await engine.import(modelId, { modelPath: url })
Defensive patterns

Strategy: validation

Validate before calling

import { isValidModelId } from './mlx-extension' // exported helper

function sanitizeModelId(raw: string): string {
  return raw.replace(/[^a-zA-Z0-9/_\-.]/g, '-').replace(/\.\./g, '')
}

const modelId = sanitizeModelId(rawName)
if (!isValidModelId(modelId)) throw new Error('Cannot sanitize model id')
await engine.import(modelId, opts)

Type guard

import { isValidModelId } from './mlx-extension'

function isSafeModelId(id: string): id is string {
  return isValidModelId(id)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/690d78c4d54f2041. Report an issue: GitHub.