# How to Handle Edge Cases When Artifacts Share URIs but Have Different Names in CMF

> Learn to handle CMF artifacts sharing URIs but having different names to prevent lineage errors and metadata drift. Understand unexpected framework behavior and resolve potential conflicts.

- Repository: [Hewlett Packard Enterprise/cmf](https://github.com/hewlettpackard/cmf)
- Tags: how-to-guide
- Published: 2026-03-03

---

**When multiple CMF artifacts share the same URI but have different names, the framework silently selects the last matching record and logs a warning to stderr, which can cause lineage errors and metadata drift.**

The Common Metadata Framework (CMF) uses URIs as the primary identifier for artifacts. Understanding how to handle edge cases when artifacts share URIs but have different names is essential for maintaining data integrity in your ML pipelines. This guide explains the collision resolution logic implemented in [`cmflib/metadata_helper.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/metadata_helper.py) and provides concrete strategies to prevent ambiguity.

## Understanding URI-Based Artifact Identification in CMF

In CMF, an artifact is uniquely identified by its **URI** stored in the `uri` column of the `artifact` table. The schema definition in [`server/app/db/dbmodels.py`](https://github.com/hewlettpackard/cmf/blob/main/server/app/db/dbmodels.py) establishes this constraint, making the URI the canonical lookup key for all artifact operations.

When you create or link artifacts using helper functions such as `create_new_artifact_event_and_attribution`, `link_execution_to_artifact`, or `link_execution_to_input_artifact`, CMF first queries the metadata store for any existing records matching the supplied URI:

```python
artifacts = store.get_artifacts_by_uri(uri)      # cmflib/metadata_helper.py L495-L505

```

## How CMF Resolves Duplicate URIs

When `get_artifacts_by_uri` returns multiple rows, the current implementation in [`cmflib/metadata_helper.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/metadata_helper.py) handles the collision by selecting the last artifact in the list and emitting a warning:

```python
if len(artifacts) > 1:
    print('Error: Found multiple artifacts with the same URI. Using the last one..',
          file=sys.stderr)                       # cmflib/metadata_helper.py L500-L502

```

This "last-one-wins" behavior means that if two logical artifacts share the same URI but have different `name` fields, downstream operations will unpredictably attach to whichever record happens to be returned last by the database query.

## Risks of Artifacts Sharing URIs with Different Names

Relying on the default collision handling creates several data integrity risks:

- **Name ambiguity** – Lineage visualizations and the CMF UI (as documented in [`docs/ui/artifacts.md`](https://github.com/hewlettpackard/cmf/blob/main/docs/ui/artifacts.md)) assume a one-to-one mapping between URIs and display names. Duplicate URIs cause the UI to show incorrect or duplicate labels.
- **Metadata drift** – When you update artifact properties via `store.put_artifacts`, the operation uses the artifact ID retrieved from the last match. This can unintentionally overwrite properties of a different logical artifact that happens to share the same URI.
- **User confusion** – The `log_dataset_with_version` method in [`cmflib/cmf_server.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/cmf_server.py) contains an explicit TODO comment: "What happens when uri is the same but names are different," indicating that this edge case is not yet fully resolved in the API design.

## Strategies to Handle Edge Cases When Artifacts Share URIs

To prevent ambiguity and ensure reliable lineage tracking, implement one of the following strategies:

### Make URIs Globally Unique

Include the artifact name or a content hash in the URI string to guarantee a one-to-one mapping. For example:

```python
uri = f"{pipeline_id}/{artifact_name}:{hash}"

```

This approach works with the existing CMF codebase without modification and is recommended for most production pipelines.

### Use Versioned URIs

Append a version suffix to the base path when storing multiple iterations of the same logical artifact:

```python
uri = f"{base_path}:v{version}"

```

This strategy preserves a clean history while maintaining uniqueness, which is useful for iterative experiments where datasets are regenerated frequently.

### Implement Strict Collision Detection

Modify the helper functions to raise an exception when multiple artifacts share a URI, forcing explicit disambiguation. Replace the warning block in [`cmflib/metadata_helper.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/metadata_helper.py) with:

```python
if len(artifacts) > 1:
    raise RuntimeError(
        f"Multiple artifacts ({len(artifacts)}) share URI '{uri}'. "
        "Disambiguate by using unique URIs or specifying the intended artifact ID."
    )

```

This approach provides safety guarantees but requires additional error handling in client code.

### Deduplicate at Ingestion

Before creating a new artifact, check for existing records and update the name field rather than creating a new row:

```python
existing = store.get_artifacts_by_uri(uri)
if existing:
    # Update existing artifact name instead of creating new

    artifact = existing[0]
    artifact.name = new_name
    store.put_artifacts([artifact])
else:
    # Create new artifact

    artifact = create_new_artifact(...)

```

This strategy treats the name as mutable metadata and maintains a single row per URI.

## End-to-End Example: Creating Unique URIs

The following example demonstrates how to construct collision-free URIs and safely link executions to artifacts:

```python
from cmflib import cmf, metadata_helper as mh

# 1. Build a unique URI that includes the artifact name and content hash

pipeline = "my-pipeline"
artifact_name = "training-data"
hash_val = "a1b2c3d4"  # e.g., computed content hash

uri = f"{pipeline}/{artifact_name}:{hash_val}"

# 2. Log the dataset (creates the artifact if it does not exist)

artifact = cmf.log_dataset_with_version(
    url="/data/train.csv",
    version=hash_val,
    event="output",
    props={"git_repo": "https://github.com/example/repo"},
    custom_properties={"stage": "raw"},
)

# 3. Link the current execution to the artifact (safe lookup)

execution_id = cmf.get_current_execution_id()
linked_artifact = mh.link_execution_to_artifact(
    store=cmf.store,
    execution_id=execution_id,
    uri=uri,
    input_name="train.csv",  # human-readable identifier for the edge

    event_type=mh.mlpb.Event.Type.INPUT,
)
print(f"Linked execution {execution_id} → artifact {linked_artifact.id}")

```

By embedding the artifact name and hash into the URI, you ensure that `store.get_artifacts_by_uri(uri)` returns exactly one record, eliminating the ambiguity described in [`cmflib/metadata_helper.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/metadata_helper.py).

## Summary

- CMF identifies artifacts solely by URI, and the helper functions in [`cmflib/metadata_helper.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/metadata_helper.py) select the last match when collisions occur, logging a warning to stderr.
- Sharing URIs between artifacts with different names causes lineage visualization errors, metadata overwrites, and UI display issues documented in [`docs/ui/artifacts.md`](https://github.com/hewlettpackard/cmf/blob/main/docs/ui/artifacts.md).
- To handle these edge cases, generate globally unique URIs by embedding artifact names or hashes, implement versioned URI schemes, or modify the collision logic to raise exceptions instead of defaulting to the last match.

## Frequently Asked Questions

### What happens if two artifacts have the same URI in CMF?

When two artifacts share a URI, the helper function `get_artifacts_by_uri` in [`cmflib/metadata_helper.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/metadata_helper.py) returns multiple rows. The current implementation selects the last artifact in the list and prints a warning to stderr stating "Found multiple artifacts with the same URI. Using the last one.." This means lineage links and metadata updates may attach to the wrong logical artifact.

### How can I prevent URI collisions in my CMF pipeline?

Prevent collisions by constructing URIs that encode uniqueness. Embed the artifact name, pipeline ID, and content hash into the URI string, such as `f"{pipeline}/{name}:{hash}"`. Alternatively, append version suffixes like `:v1` or `:v2` to distinguish between iterations of the same logical asset. These patterns ensure that `store.get_artifacts_by_uri` returns exactly one match.

### Is it safe to modify the URI generation logic in CMF?

Yes, modifying URI generation is the recommended approach for handling edge cases. The `log_dataset_with_version` method in [`cmflib/cmf_server.py`](https://github.com/hewlettpackard/cmf/blob/main/cmflib/cmf_server.py) contains a TODO comment indicating that the development team recognizes the need to handle cases where "uri is the same but names are different." By implementing unique URI schemes at the application level, you avoid changing core CMF library code while ensuring data integrity.

### How does the CMF UI handle duplicate URIs?

The CMF UI, documented in [`docs/ui/artifacts.md`](https://github.com/hewlettpackard/cmf/blob/main/docs/ui/artifacts.md), assumes a one-to-one mapping between URIs and display entries. When multiple artifacts share a URI, the UI typically displays only the last record or a single merged entry, effectively hiding the duplicate names from the user. This can lead to confusion when the displayed artifact name does not match the expected logical artifact in your pipeline.