{"record":{"id":"0dd2dce949ed8546","repo":"glanceapp/glance","slug":"url-is-required","errorCode":null,"errorMessage":"URL is required","messagePattern":"URL is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/glance/widget-extension.go","lineNumber":35,"sourceCode":"\nconst extensionWidgetDefaultTitle = \"Extension\"\n\ntype extensionWidget struct {\n\twidgetBase          `yaml:\",inline\"`\n\tURL                 string               `yaml:\"url\"`\n\tFallbackContentType string               `yaml:\"fallback-content-type\"`\n\tParameters          queryParametersField `yaml:\"parameters\"`\n\tHeaders             map[string]string    `yaml:\"headers\"`\n\tAllowHtml           bool                 `yaml:\"allow-potentially-dangerous-html\"`\n\tExtension           extension            `yaml:\"-\"`\n\tcachedHTML          template.HTML        `yaml:\"-\"`\n}\n\nfunc (widget *extensionWidget) initialize() error {\n\twidget.withTitle(extensionWidgetDefaultTitle).withCacheDuration(time.Minute * 30)\n\n\tif widget.URL == \"\" {\n\t\treturn errors.New(\"URL is required\")\n\t}\n\n\tif _, err := url.Parse(widget.URL); err != nil {\n\t\treturn fmt.Errorf(\"parsing URL: %v\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (widget *extensionWidget) update(ctx context.Context) {\n\textension, err := fetchExtension(extensionRequestOptions{\n\t\tURL:                 widget.URL,\n\t\tFallbackContentType: widget.FallbackContentType,\n\t\tParameters:          widget.Parameters,\n\t\tHeaders:             widget.Headers,\n\t\tAllowHtml:           widget.AllowHtml,\n\t})\n","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/glanceapp/glance/blob/91324e8de762702e97b0ac5c8e36271d644d8642/internal/glance/widget-extension.go#L17-L53","documentation":"Thrown by the extension widget's initialize() when no url field is set. Extensions are remote page fragments loaded from a URL (with a default 30-minute cache), so the URL is the only mandatory field; an unset one is rejected immediately.","triggerScenarios":"A widget of type: extension with only fallback-content-type, parameters, or headers configured; url misspelled (uri:, link:) or commented out; url present only in a copy of the widget that was renamed.","commonSituations":"Prototyping an extension entry and commenting the url out to 'disable' it; indentation placing url under parameters.","solutions":["Set url: to the extension fragment's address, e.g. url: https://example.com/extension.html","Verify the key is exactly 'url' and at the widget's top level","To actually disable an extension widget, remove or comment out the whole widget block instead of just the url"],"exampleFix":"# before\n- type: extension\n  # url: https://example.com/fragment.html\n  parameters:\n    key: value\n\n# after\n- type: extension\n  url: https://example.com/fragment.html\n  parameters:\n    key: value","handlingStrategy":"validation","validationCode":"if w['type'] == 'extension' and not w.get('url'):\n    raise ValueError('extension widget requires url')","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write the url first when adding an extension widget","Disable extensions by removing the whole block, not by deleting one key","Lint for required keys per widget type in CI"],"tags":["config","yaml","extension","validation"],"backgroundTag":null,"analyzedSha":"91324e8de762702e97b0ac5c8e36271d644d8642","analyzedAt":"2026-08-15T14:12:54.279Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}