{"record":{"id":"70ad58edc92030a3","repo":"windmill-labs/windmill","slug":"invalid-source-object-typeof-resolvedconfig-sourc","errorCode":null,"errorMessage":"Invalid source object${typeof resolvedConfig.source}","messagePattern":"Invalid source object(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/apps/components/display/AppDownload.svelte","lineNumber":93,"sourceCode":"\t\t\t)\n\t\t}\n\t}\n\n\tasync function loadSource() {\n\t\tif (isPartialS3Object(resolvedConfig.source)) {\n\t\t\tdownloadUrl = await getS3File({\n\t\t\t\tsource: resolvedConfig.source.s3,\n\t\t\t\tstorage: resolvedConfig.source.storage,\n\t\t\t\tpresigned: resolvedConfig.source.presigned,\n\t\t\t\tappPath: $appPath,\n\t\t\t\tusername: $userStore?.username,\n\t\t\t\tworkspace,\n\t\t\t\ttoken: token?.token,\n\t\t\t\tisEditor,\n\t\t\t\tconfiguration\n\t\t\t})\n\t\t} else if (resolvedConfig.source && typeof resolvedConfig.source !== 'string') {\n\t\t\tthrow new Error('Invalid source object' + typeof resolvedConfig.source)\n\t\t} else if (resolvedConfig.source?.startsWith('s3://')) {\n\t\t\tdownloadUrl = await getS3File({\n\t\t\t\tsource: resolvedConfig.source?.replace('s3://', ''),\n\t\t\t\tappPath: $appPath,\n\t\t\t\tusername: $userStore?.username,\n\t\t\t\tworkspace,\n\t\t\t\ttoken: token?.token,\n\t\t\t\tisEditor,\n\t\t\t\tconfiguration\n\t\t\t})\n\t\t} else {\n\t\t\tdownloadUrl = transformBareBase64IfNecessary(resolvedConfig.source)\n\t\t}\n\t}\n\n\tlet css = $state(initCss($app.css?.downloadcomponent, untrack(() => customCss)))\n\t$effect(() => {\n\t\tresolvedConfig.beforeIcon && beforeIconComponent && untrack(() => handleBeforeIcon())","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/apps/components/display/AppDownload.svelte#L75-L111","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change the component's source configuration so it evaluates to a string URL (or s3:// path)","Inspect what the bound expression returns — stringify it or select the URL field of the returned object","If using a script to produce the source, return `url` as a plain string"],"exampleFix":"// before\nsource: myScriptResult   // object\n// after\nsource: myScriptResult.url  // string, e.g. 'https://.../file.pdf' or 's3://bucket/key'","handlingStrategy":"type-guard","validationCode":"if (resolvedConfig.source && typeof resolvedConfig.source !== 'string') {\n  throw new Error(`source must be a string URL or s3:// path, got ${typeof resolvedConfig.source}`)\n}","typeGuard":"function isDownloadSource(s: unknown): s is string {\n  return typeof s === 'string' && s.length > 0\n}","tryCatchPattern":"try {\n  await loadSource()\n} catch (e) {\n  if (String(e.message).startsWith('Invalid source object')) {\n    downloadError = 'Configure the download source as a string URL'\n  } else throw e\n}","preventionTips":["Ensure bound expressions/script outputs for `source` return strings, not objects","Use input templates that interpolate to URL strings","Validate component configuration in the app editor before publishing"],"tags":["apps","configuration","download","typeerror"],"backgroundTag":"invalid-prop-type","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}