QuantumNous/new-api · warning · Error

JSON must be an object

Error message

JSON must be an object

What it means

Thrown by normalizeJsonObject in the update-config dialog when parsing env_json or secret_env_json succeeds but the result is not a plain object (null, array, or primitive). It is a plain English literal (not i18n'd) raised during form submit/normalization before the config update is sent.

Source

Thrown at web/src/features/models/components/dialogs/update-config-dialog.tsx:69

  traffic_port: z.coerce.number().int().min(1).max(65535).optional(),
  entrypoint: z.string().optional(),
  args: z.string().optional(),
  command: z.string().optional(),
  registry_username: z.string().optional(),
  registry_secret: z.string().optional(),
  env_json: z.string().optional(),
  secret_env_json: z.string().optional(),
})

type Values = z.input<typeof schema>

const UPDATE_CONFIG_FORM_ID = 'update-config-form'

function normalizeJsonObject(input?: string) {
  if (!input || !input.trim()) return undefined
  const parsed = JSON.parse(input)
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new Error('JSON must be an object')
  }
  return Object.fromEntries(
    Object.entries(parsed as Record<string, unknown>).map(([k, v]) => [
      k,
      String(v),
    ])
  ) as Record<string, string>
}

export function UpdateConfigDialog({
  open,
  onOpenChange,
  deploymentId,
}: {
  open: boolean
  onOpenChange: (open: boolean) => void
  deploymentId: string | number | null
}) {

View on GitHub (pinned to e2c7aa7b10)

Solutions

  1. Use a top-level object with string values: {"ENV_NAME": "value"}
  2. Wrap arrays as an object keyed by name instead of a list
  3. Leave the field blank/empty to omit it — blank returns undefined without parsing
  4. Ensure all values are strings (the normalizer String()s them, but start clean)

Example fix

// before
secret_env_json: '["API_KEY=xyz"]'
// after
secret_env_json: '{"API_KEY": "xyz"}'
Defensive patterns

Strategy: validation

Validate before calling

const isJsonObjectString = (s?: string): boolean => {
  if (!s?.trim()) return true
  try {
    const v: unknown = JSON.parse(s)
    return !!v && typeof v === 'object' && !Array.isArray(v)
  } catch { return false }
}
// disable submit unless isJsonObjectString(values.env_json) && isJsonObjectString(values.secret_env_json)

Type guard

const isPlainObject = (v: unknown): v is Record<string, unknown> =>
  typeof v === 'object' && v !== null && !Array.isArray(v)

Try / catch

try {
  const env = normalizeJsonObject(values.env_json)
  const secretEnv = normalizeJsonObject(values.secret_env_json)
  // proceed with update
} catch (error) {
  toast.error((error as Error).message) // 'JSON must be an object' or JSON.parse SyntaxError
}

Prevention

When it happens

Trigger: Entering env_json like ["A=1"] or "A=1" (bare string) or null in the update-config form; JSON.parse succeeds, the object check fails, submission aborts.

Common situations: Users paste a JSON array of env entries or a single-quoted string; copy env objects from docker-compose YAML converted incorrectly; assume null clears the field (empty string is the supported way).

Related errors


AI-assisted analysis of QuantumNous/new-api@e2c7aa7b10 (2026-08-15). Data as JSON: /api/errors/3e456e1d355ecf02. Report an issue: GitHub.