{"record":{"id":"f022ad6a947e0b46","repo":"HumanSignal/label-studio","slug":"task-is-required-to-resolve-uri-uri","errorCode":null,"errorMessage":"Task is required to resolve URI={uri}","messagePattern":"Task is required to resolve URI=(.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"label_studio/io_storages/base_models.py","lineNumber":419,"sourceCode":"        elif isinstance(uri, dict):\n            resolved = {}\n            for key in uri.keys():\n                result = self.resolve_uri(uri[key], task)\n                resolved[key] = result if result else uri[key]\n            return resolved\n\n        # string: process one url\n        elif isinstance(uri, str) and self.url_scheme in uri:\n            try:\n                # extract uri first from task data\n                extracted_uri, _ = get_uri_via_regex(uri, prefixes=(self.url_scheme,))\n                if not self.can_resolve_url(extracted_uri):\n                    logger.debug(f'No storage info found for URI={uri}')\n                    return\n\n                if task is None:\n                    logger.error(f'Task is required to resolve URI={uri}', exc_info=True)\n                    raise ValueError(f'Task is required to resolve URI={uri}')\n\n                proxy_url = urljoin(\n                    settings.HOSTNAME,\n                    reverse('storages:task-storage-data-resolve', kwargs={'task_id': task.id})\n                    + f'?fileuri={base64.urlsafe_b64encode(extracted_uri.encode()).decode()}',\n                )\n                return uri.replace(extracted_uri, proxy_url)\n            except Exception:\n                logger.info(f\"Can't resolve URI={uri}\", exc_info=True)\n\n    def resolve_uris(self, data, task):\n        \"\"\"Resolve all cloud storage URIs in data, replacing each with a proxy URL.\n\n        Unlike resolve_uri which only handles the first URI in a string,\n        this finds and replaces every URI matching this storage's scheme.\n        Handles str, list, and dict data recursively.\n\n        Returns the resolved data, or None if nothing was resolved.","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/base_models.py#L401-L437","documentation":"When resolving storage URIs into servable URLs, the storage needs a Task to build the per-task proxy URL (via the task-storage-data-resolve route). resolve_uri raises this ValueError when can_resolve_url matched the URI but the task argument is None, because the proxy link cannot be constructed without a task id.","triggerScenarios":"Calling resolve_uri(uri, task=None) (or a caller propagating a missing task) for a URI whose scheme matches the storage's url_scheme and passes can_resolve_url.","commonSituations":"Resolving URIs outside of a task context (e.g. in import previews or export tooling) where no Task instance exists; passing task=None by default in wrapper utilities.","solutions":["Pass the Task instance that owns the data (task.id is used to build the proxy URL).","Only call resolve_uri when you actually have a task context; otherwise skip resolution for that URI.","If building tooling, fetch the task first (Task.objects.get) or resolve the file directly via the storage's HTTP URL generator instead."],"exampleFix":"// before\nuri = storage.resolve_uri('s3://bucket/file.jpg', task=None)\n\n// after\ntask = Task.objects.get(id=task_id)\nuri = storage.resolve_uri('s3://bucket/file.jpg', task=task)","handlingStrategy":"validation","validationCode":"if task is None:\n    raise ValueError('resolve_uri requires a Task instance to build the proxy URL')\nuri_to_resolve = uri\nif not storage.can_resolve_url(uri_to_resolve):\n    return  # nothing to resolve","typeGuard":"def has_task(task) -> bool:\n    return task is not None and getattr(task, 'id', None) is not None","tryCatchPattern":"try:\n    url = storage.resolve_uri(uri, task=task)\nexcept ValueError as e:\n    if 'Task is required' in str(e):\n        url = None  # resolution requires task context; skip or handle manually\n    else:\n        raise","preventionTips":["Always thread the Task instance through URI-resolution helpers.","Skip URI resolution entirely when operating outside a task context.","Assert task.id exists before calling resolve_uri (unsaved tasks have no id)."],"tags":["storage","uri-resolution","missing-argument"],"backgroundTag":"missing-required-task-context","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}