{"record":{"id":"f8a69e7bf316823e","repo":"Budibase/budibase","slug":"unable-to-split-value-of-type-typeof-value-v","errorCode":null,"errorMessage":"Unable to split value of type ${typeof value}: ${value}","messagePattern":"Unable to split value of type (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/automations/automationUtils.ts","lineNumber":336,"sourceCode":"      .replace(/loop/, substitute)\n    substitutedHbsString += before + block\n    pointer = closedPointer\n  }\n  if (checkForJS) {\n    substitutedHbsString = encodeJSBinding(substitutedHbsString)\n  }\n  return substitutedHbsString\n}\n\nexport function stringSplit(value: string | string[]) {\n  if (value == null) {\n    return []\n  }\n  if (Array.isArray(value)) {\n    return value\n  }\n  if (typeof value !== \"string\") {\n    throw new Error(`Unable to split value of type ${typeof value}: ${value}`)\n  }\n  const splitOnNewLine = value.split(\"\\n\")\n  if (splitOnNewLine.length > 1) {\n    return splitOnNewLine\n  }\n  return value.split(\",\")\n}\n\nexport function matchesLoopFailureCondition(\n  step: LoopV2Step,\n  currentItem: any\n) {\n  const { failure } = step.inputs\n  if (!failure) {\n    return false\n  }\n\n  if (isPlainObject(currentItem)) {","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/automations/automationUtils.ts#L318-L354","documentation":"stringSplit converts a loop's iterable string into an array by splitting on newlines or commas. It accepts arrays and strings only; any other value type (number, object, null handled earlier, boolean) causes an Error stating the received type and value.","triggerScenarios":"A loop step's iterable binding resolves to a number, object, or boolean instead of a string or array — e.g. iterating a numeric field or an object from a query output.","commonSituations":"Binding to a table column of number type; a REST query returning an object instead of a list; binding resolution producing a single non-string primitive.","solutions":["Wrap the value as a string in the binding (e.g. use string conversion in the loop iterable) or select a string column.","Point the loop at an array output (e.g. query rows array) instead of a scalar.","Change the source column/field type to text, or use a script step to shape the data before the loop.","Inspect the trigger/step outputs to confirm the iterable's runtime type."],"exampleFix":"// before\niterations: {{trigger.count}}  // number\n// after\niterations: \"{{trigger.count}}\"  // string, or pass an array","handlingStrategy":"type-guard","validationCode":"const iterable = loop.iterable\nif (typeof iterable !== \"string\" && !Array.isArray(iterable)) {\n  throw new Error(`Loop iterable must be string or array, got ${typeof iterable}`)\n}","typeGuard":"const isSplittable = (v: unknown): v is string | unknown[] =>\n  typeof v === \"string\" || Array.isArray(v)","tryCatchPattern":"try {\n  outputs = await runLoopStep(loopInputs)\n} catch (e) {\n  if (/Unable to split value of type/.test(e.message)) {\n    // coerce the binding to string or select an array output\n  } else throw e\n}","preventionTips":["Bind loops to array outputs (query rows, table rows) whenever possible.","Convert numeric/scalar bindings to strings explicitly.","Check trigger output types in the builder before using them as iterables.","Use a script step to normalize data shapes before loops."],"tags":["automation","loop","type-error","validation"],"backgroundTag":"wrong-argument-type","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}