windmill-labs/windmill · error · Error

An error occurred

Error message

An error occurred

What it means

The generated 'fetch' starter script for Windmill replaces any failure from fetch() or res.json() with a generic Error('An error occurred'). The real cause (network failure, DNS error, HTTP 4xx/5xx status, or a non-JSON response body that breaks JSON parsing) is swallowed by the catch with no argument, so the developer only sees the opaque message.

Source

Thrown at frontend/src/lib/script_helpers.ts:556

	}

	const requestOptions: RequestInit = {
		method: method || 'GET',
		headers: headers || {}
	}

	if (requestOptions.method !== 'GET' && requestOptions.method !== 'HEAD' && body !== undefined) {
		requestOptions.body = JSON.stringify(body)
		requestOptions.headers = {
			'Content-Type': 'application/json',
			...requestOptions.headers
		}
	}

	return await fetch(url, requestOptions)
		.then((res) => res.json())
		.catch(() => {
			throw new Error('An error occurred')
		})
}`

const BASH_INIT_CODE = `# shellcheck shell=bash
# arguments of the form X="$I" are parsed as parameters X of type string
msg="$1"
dflt="\${2:-default value}"

# the last line of the stdout is the return value
# unless you write json to './result.json' or a string to './result.out'
echo "Hello $msg"
`

const DENO_INIT_CODE_TRIGGER = `import * as wmill from "npm:windmill-client@${__pkg__.version}"

export async function main() {

  // A common trigger script would follow this pattern:

View on GitHub (pinned to e474e8803c)

Solutions

  1. Log the actual error and response status instead of discarding it: catch((err) => { console.error(err); throw err })
  2. Check res.ok / res.status before calling res.json() and surface the body text on failure
  3. Verify the target URL is reachable from the worker (curl it from the same environment)
  4. If the response may not be JSON, use res.text() and parse conditionally

Example fix

// before
return await fetch(url, requestOptions)
  .then((res) => res.json())
  .catch(() => {
    throw new Error('An error occurred')
  })
// after
const res = await fetch(url, requestOptions)
if (!res.ok) {
  throw new Error(`HTTP ${res.status}: ${await res.text()}`)
}
return await res.json()
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling the script/fetch
if (!url || !/^https?:\/\//.test(url)) {
  throw new Error(`Invalid URL: ${url}`)
}

Type guard

function isErrorResponse(res: Response): boolean {
  return !res.ok
}

Try / catch

try {
  const res = await fetch(url, requestOptions)
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`)
  return await res.json()
} catch (err) {
  // preserve the original cause instead of masking it
  throw new Error(`Fetch to ${url} failed: ${err instanceof Error ? err.message : err}`)
}

Prevention

When it happens

Trigger: Running the FETCH_INIT_CODE starter script when: the url is unreachable or blocked (network/CORS), the server returns a non-2xx status (fetch does not throw on HTTP errors), or the response body is not valid JSON so res.json() rejects.

Common situations: Typing a wrong or internal-only URL; calling an API that returns HTML error pages or empty bodies; missing auth headers so the server returns 401 with a non-JSON body; TypeScript/Deno environments where fetch fails on TLS or DNS.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/0a1e636a06ecbecc. Report an issue: GitHub.