windmill-labs/windmill · error · Error

Invalid source object${typeof resolvedConfig.source}

Error message

Invalid source object${typeof resolvedConfig.source}

What it means

AppDownload resolves its configured `source` into a downloadable URL. A string source (including s3:// paths) is supported, but if the resolved config yields a non-string, non-falsy source object the component throws, since it has no way to turn an object into a download URL.

Source

Thrown at frontend/src/lib/components/apps/components/display/AppDownload.svelte:93

			)
		}
	}

	async function loadSource() {
		if (isPartialS3Object(resolvedConfig.source)) {
			downloadUrl = await getS3File({
				source: resolvedConfig.source.s3,
				storage: resolvedConfig.source.storage,
				presigned: resolvedConfig.source.presigned,
				appPath: $appPath,
				username: $userStore?.username,
				workspace,
				token: token?.token,
				isEditor,
				configuration
			})
		} else if (resolvedConfig.source && typeof resolvedConfig.source !== 'string') {
			throw new Error('Invalid source object' + typeof resolvedConfig.source)
		} else if (resolvedConfig.source?.startsWith('s3://')) {
			downloadUrl = await getS3File({
				source: resolvedConfig.source?.replace('s3://', ''),
				appPath: $appPath,
				username: $userStore?.username,
				workspace,
				token: token?.token,
				isEditor,
				configuration
			})
		} else {
			downloadUrl = transformBareBase64IfNecessary(resolvedConfig.source)
		}
	}

	let css = $state(initCss($app.css?.downloadcomponent, untrack(() => customCss)))
	$effect(() => {
		resolvedConfig.beforeIcon && beforeIconComponent && untrack(() => handleBeforeIcon())

View on GitHub (pinned to e474e8803c)

Solutions

  1. Change the component's source configuration so it evaluates to a string URL (or s3:// path)
  2. Inspect what the bound expression returns — stringify it or select the URL field of the returned object
  3. If using a script to produce the source, return `url` as a plain string

Example fix

// before
source: myScriptResult   // object
// after
source: myScriptResult.url  // string, e.g. 'https://.../file.pdf' or 's3://bucket/key'
Defensive patterns

Strategy: type-guard

Validate before calling

if (resolvedConfig.source && typeof resolvedConfig.source !== 'string') {
  throw new Error(`source must be a string URL or s3:// path, got ${typeof resolvedConfig.source}`)
}

Type guard

function isDownloadSource(s: unknown): s is string {
  return typeof s === 'string' && s.length > 0
}

Try / catch

try {
  await loadSource()
} catch (e) {
  if (String(e.message).startsWith('Invalid source object')) {
    downloadError = 'Configure the download source as a string URL'
  } else throw e
}

Prevention

When it happens

Trigger: The app's download component configuration resolves `source` to an object (e.g. a evaluated/templated expression returning an object, or a misconfigured resource binding) — neither undefined nor a string — so loadSource hits the `typeof !== 'string'` branch.

Common situations: Binding the source field to a script/flow output that returns an object instead of a URL string; using an input template that interpolates into an object; a configuration left half-migrated between source formats.

Related errors


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