mudler/LocalAI · error · Error

No job ID returned from server

Error message

No job ID returned from server

What it means

ImportModel.jsx calls modelsApi.importUri to start a URI-based (e.g. HuggingFace) model import and expects the response to carry a job identifier under uuid or ID, which startJobPolling then tracks. When neither field is present the client cannot poll the job, so it throws rather than showing a fake 'started' state. The next lines' ambiguity branch (HTTP 400 {error:'ambiguous import'}) is a different, handled path — this throw is for a 2xx response with an unexpected body.

Source

Thrown at core/http/react-ui/src/pages/ImportModel.jsx:307

      if (prefs.enable_parameters.trim()) prefsObj.enable_parameters = prefs.enable_parameters.trim()
      if (prefs.cuda) prefsObj.cuda = true
      customPrefs.forEach(cp => {
        if (cp.key.trim() && cp.value.trim()) prefsObj[cp.key.trim()] = cp.value.trim()
      })

      const result = await modelsApi.importUri({
        uri: importUri.trim(),
        preferences: Object.keys(prefsObj).length > 0 ? prefsObj : null,
      })

      const hasSize = result.estimated_size_display && result.estimated_size_display !== '0 B'
      const hasVram = result.estimated_vram_display && result.estimated_vram_display !== '0 B'
      if (hasSize || hasVram) {
        setEstimate({ sizeDisplay: result.estimated_size_display || '', vramDisplay: result.estimated_vram_display || '' })
      }

      const jobId = result.uuid || result.ID
      if (!jobId) throw new Error('No job ID returned from server')

      addToast(t('toasts.started'), 'success')
      // Clear any prior ambiguity alert once the server accepts the import.
      setAmbiguity(null)
      startJobPolling(jobId)
    } catch (err) {
      // Structured ambiguity response — render the inline picker instead of
      // a toast. The server returns HTTP 400 with { error, modality,
      // candidates } which api.handleResponse attaches as err.body.
      if (err?.status === 400 && err?.body && err.body.error === 'ambiguous import') {
        setAmbiguity({
          modality: err.body.modality || '',
          candidates: Array.isArray(err.body.candidates) ? err.body.candidates : [],
        })
        setIsSubmitting(false)
        return
      }
      addToast(t('toasts.startImportFailed', { message: err.message }), 'error')

View on GitHub (pinned to 44413a9d06)

Solutions

  1. Align versions: run a LocalAI server of the same generation as the React UI (rebuild/update the backend)
  2. Inspect the actual response: console.log(result) or curl -X POST $BASE/importuri to see which field name the server uses
  3. If the server completed synchronously, skip polling: treat presence of the model in /v1/models as success instead of throwing
  4. Add the server's field name to the jobId lookup (see exampleFix)

Example fix

// before
const jobId = result.uuid || result.ID
if (!jobId) throw new Error('No job ID returned from server')

// after — tolerate shape drift
const jobId = result.uuid || result.ID || result.id || result.job?.uuid
if (!jobId) throw new Error('No job ID returned from server')
Defensive patterns

Strategy: validation

Validate before calling

// defensive shape check before relying on the job id
function extractJobId(result) {
  if (!result || typeof result !== 'object') return null
  return result.uuid ?? result.ID ?? result.id ?? null
}

Type guard

function isImportJobResponse(result) {
  return Boolean(result) && typeof result === 'object' && ('uuid' in result || 'ID' in result)
}

Try / catch

try {
  const result = await modelsApi.importUri({ uri, preferences })
  const jobId = result.uuid || result.ID || result.id
  if (!jobId) throw new Error('No job ID returned from server')
  startJobPolling(jobId)
} catch (err) {
  if (err?.status === 400 && err?.body?.error === 'ambiguous import') setAmbiguity(err.body)
  else if (err.message === 'No job ID returned from server') addToast('server version mismatch — update LocalAI', 'error')
  else addToast(err.message, 'error')
}

Prevention

When it happens

Trigger: The server accepts the import (2xx) but returns a body without uuid/ID: version mismatch where an older/newer backend renamed the field (e.g. returns {id: ...} or {job: ...}), a proxy rewriting the response, or a server that synchronously completed the import and returned a different shape. The field check result.uuid || result.ID covers both casings, so genuinely missing means shape drift.

Common situations: React UI (newer) talking to an older LocalAI binary whose import-uri endpoint predates job-based responses; response mangled by an intercepting proxy; API contract changed in a fork.

Related errors


AI-assisted analysis of mudler/LocalAI@44413a9d06 (2026-08-15). Data as JSON: /api/errors/8058e1bf102820c2. Report an issue: GitHub.