gatsbyjs/gatsby · error · Error

An ID must be provided when creating or setting job

Error message

An ID must be provided when creating or setting job

What it means

The jobs reducer (v1 job API) handles CREATE_JOB and SET_JOB by merging or pushing into state.jobs.active, keyed by payload.id. It throws at the top of that case if action.payload.id is falsy, because without an ID the job cannot be found, merged, deduplicated, or later ended. The ID is the correlation key for the entire job lifecycle.

Source

Thrown at packages/gatsby/src/redux/reducers/jobs.ts:15

import _ from "lodash"
import { oneLine } from "common-tags"
import moment from "moment"

import { IGatsbyState, ActionsUnion } from "../types"

export const jobsReducer = (
  state: IGatsbyState["jobs"] = { active: [], done: [] },
  action: ActionsUnion
): IGatsbyState["jobs"] => {
  switch (action.type) {
    case `CREATE_JOB`:
    case `SET_JOB`: {
      if (!action.payload.id) {
        throw new Error(`An ID must be provided when creating or setting job`)
      }
      const index = _.findIndex(state.active, j => j.id === action.payload.id)
      if (index !== -1) {
        const mergedJob = _.merge(state.active[index], {
          ...action.payload,
          createdAt: Date.now(),
          plugin: action.plugin,
        })

        state.active[index] = mergedJob
        return state
      } else {
        state.active.push({
          ...action.payload,
          createdAt: Date.now(),
          plugin: action.plugin,
        })
        return state

View on GitHub (pinned to 8b06340921)

Solutions

  1. Always pass a stable, unique `id` (e.g. derived from a content digest) when creating/setting a job.
  2. Prefer the v2 job API (createJobV2) over the legacy jobs actions; it validates inputs differently.
  3. If you do not need background job tracking, remove the call entirely.

Example fix

// before
actions.createJob({ name: `resize`, outputDir }) // no id
// after
actions.createJob({ id: createContentDigest({ outputDir, input }), name: `resize`, outputDir })
Defensive patterns

Strategy: validation

Validate before calling

// Always derive a stable id before createJob/setJob.
const crypto = require('crypto')
function jobId(input) {
  if (!input || (typeof input !== 'object')) throw new Error('job input required')
  return input.id || crypto.createHash('sha1').update(JSON.stringify(input)).digest('hex')
}
// actions.createJob({ id: jobId(payload), ...payload })

Type guard

function isValidJobPayload(p) {
  return !!p && typeof p === 'object' && typeof p.id === 'string' && p.id.length > 0
}

Prevention

When it happens

Trigger: Dispatching (or having a plugin dispatch) a CREATE_JOB / SET_JOB action whose payload lacks `id`. Normally reached only via internal helper APIs like actions.createJob / actions.setJob that wrap the dispatch; passing no/empty id to those wrappers reproduces it.

Common situations: A custom plugin calling the deprecated internal job API (jobs.createJob/setJob) without an id; a third-party plugin written against an older internal API surface; programmatic misuse of the redux store directly.

Related errors


AI-assisted analysis of gatsbyjs/gatsby@8b06340921 (2026-08-13). Data as JSON: /api/errors/33ef50d5a5ebaa7f. Report an issue: GitHub.