ErrLookup › Background articles › NOT_FOUND error code: why tRPC, Harbor, Nacos and other libraries return 404 "not found" errors for resources that may still exist
NOT_FOUND error code: why tRPC, Harbor, Nacos and other libraries return 404 "not found" errors for resources that may still exist
"NOT_FOUND" is the error code a large family of libraries attach to the 404-equivalent condition: the resource named in the request could not be resolved. Developers meet it as tRPC's TRPCError NOT_FOUND ("No \"query\"-procedure on path ..."), Harbor's NotFound errors on artifacts, blobs and replication executions, shadcn's registry item lookups, Nacos agent/MCP-server version fetches, and scope-filtered model lookups in LobeHub, Dokploy, Medusa and RAGFlow. The single most important thing to know: the resource being missing is only one cause — the same error fires for stale ids, wrong ownership scope, version skew between client and server, and cases where libraries deliberately hide "exists but forbidden" behind a not-found response.
Distilled from 97 documented records across 11 repositories.
Background
NOT_FOUND sits at the application layer, not the transport layer: it is a semantic decision by the server that the requested entity does not resolve, mapped onto HTTP 404 or an equivalent structured error code. The typical producer is a lookup function — a repository `findById`, a config-store query, a registry catalog scan, a router path dispatch — that returns null/undefined/zero rows, after which the caller throws a typed error carrying the id, name, or path that failed. In tRPC this is the server-side cousin of a 404: `callProcedure` throws NOT_FOUND with `No "<type>"-procedure on path "<path>"` when the path doesn't exist or the procedure kind doesn't match. In Harbor it is an error code on a controller error (`artifact %s:%s not found`, `replication execution %d not found`) that maps to HTTP 404. In shadcn's registry loader it is a RegistryItemNotFoundError when an item name isn't declared in the resolved catalog.
From the caller's side the error is usually deterministic and terminal — nearly every record says "do not retry, refresh the id source instead." But the underlying meaning varies more than the message suggests. One large subgroup is the scope-filtered lookup: LobeHub, RAGFlow, Medusa and others construct their models with userId/workspaceId (or tenant) filters, so a resource owned by another user or workspace is indistinguishable from one that never existed. RAGFlow is explicit that this is deliberate — a single NotFound covers both "missing" and "forbidden" to avoid leaking other tenants' memories, and LobeHub's device guard does the same to prevent existence inference. ThingsBoard returns NOT_FOUND rather than FAILURE on bad provisioning credentials for the same anti-enumeration reason.
A second subgroup is timing races. Harbor's garbage collector reports "no blob found to mark delete failed" or "no blob item is updated to StatusNone" when the database row vanished between candidate selection and the status UPDATE — almost always overlapping GC executions or an external process deleting rows. More generally, any flow that lists resources and then acts on them by id (LobeHub briefs, knowledge bases, topics; Harbor tags) can hit NOT_FOUND when the row is deleted in between, and most libraries advise treating that branch as idempotent success.
Finally, NOT_FOUND often means "wrong shape of request," not "missing thing." tRPC throws it when client and server router shapes drifted after a rename or a version skew, or when a query path is called with `.mutate()`. shadcn throws "The item at ... was not found" when a local file path reaches the namespace-resolution branch instead of `loadRegistry()`. Harbor's replication-execution lookup filters by vendor type, so an id belonging to a different job kind also reports not found, and Nacos distinguishes agent-not-found from version-not-found when a version was never published or was pruned. The family's shared lesson: verify what identifier, scope, and shape the server actually resolves before concluding the resource is gone.
Common causes
- Stale or deleted identifier. The id was valid earlier but the row was deleted between listing and use — a deleted connector, brief, topic, knowledge base, skill, redis database, customer, or thread. Most libraries recommend re-listing and treating NOT_FOUND as terminal rather than retrying the same id.
- Wrong ownership or workspace scope. Scope-filtered lookups (userId/workspaceId/tenant) make a resource owned by someone else indistinguishable from a non-existent one. LobeHub, RAGFlow, and Medusa all behave this way; the fix is to confirm the caller's scope matches where the resource was created, not to hunt for a missing row.
- Version skew between client and server. tRPC throws NOT_FOUND when the client calls a path the running server doesn't have — a rename, removal, or different major version so the router shape drifted. Redeploying both sides together and matching call kind (.query/.mutate/.subscribe) resolves it.
- Deliberate existence-hiding. Some servers intentionally conflate "exists but forbidden" with "not found" to prevent enumeration: RAGFlow memories, LobeHub private workspace devices, and ThingsBoard's provisioning credentials all return a plain NOT_FOUND. Distinguishing them in the UI defeats the design.
- Concurrent deletion race. Harbor's GC reports NOT_FOUND when a blob row disappears between candidate selection and a status UPDATE — typically overlapping GC runs or an external process deleting rows. The blob is usually already gone, so the error is benign and the next sweep won't see it.
- Wrong identifier type or malformed input. Passing an agent id where a chat-group id is expected (LobeHub), a replication execution id of another vendor type (Harbor), a local file path where a registry name is expected (shadcn), a wrong-cased provisioning field (ThingsBoard), or a mistyped/truncated UUID all produce the same NOT_FOUND.
- Unpublished or pruned version. Nacos returns NOT_FOUND when a specific agent version or MCP server version was never published, was deleted, or its detail config was lost while the version index entry remains. Fetching the latest version or listing available versions first avoids it.
What usually fixes it
- Re-source identifiers from a fresh list call in the correct scope (workspace, user, tenant) before acting on them, and treat NOT_FOUND as a signal to refresh client state — close the form, drop the id, re-list — rather than to retry.
- Treat NOT_FOUND as deterministic and terminal in retry loops; for delete-like or idempotent operations, absorb it as success when the end state is already correct (e.g. Harbor blob Touch, LobeHub brief delete).
- Keep clients and servers in shape agreement: deploy both sides together after renames, stay on the same tRPC major version, match the call kind to the procedure kind, and cover documented procedures with integration tests.
- Serialize background jobs that delete the rows other flows depend on — run one GC at a time via the library's lock, and stop or find external processes that delete rows concurrently.
- Verify request shape before dispatch: use the right loader for local paths vs registries, the right id type for the target resource, exact case-sensitive field names, and double-URL-encode repository names containing '/'.
- For auth-sensitive resources, handle 404 as also meaning "exists but not shared with you": request the right permission (e.g. RAGFlow TEAM permission), confirm workspace membership, and revoke long-lived tokens when the referenced account is deleted (Medusa customer tokens).
Go deeper
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Documented occurrences
- NOT_FOUND: no blob found to mark delete failed, ID:%d, digest:%s (goharbor/harbor)
- NOT_FOUND: Connector not found (lobehub/lobehub)
- NOT_FOUND: No "${type}"-procedure on path "${path}" (trpc/trpc)
- NOT_FOUND: Customer with id: ${id} was not found (medusajs/medusa)
- NOT_FOUND: Personal device not found. (lobehub/lobehub)
- NOT_FOUND: Workspace device not found. (lobehub/lobehub)
- NOT_FOUND: Credential not found: ${input.key} (lobehub/lobehub)
- NOT_FOUND: no blob item is updated to StatusNone, id:%d, digest:%s (goharbor/harbor)
- NOT_FOUND: The item at ${url} was not found. It may not exist at the registry. (shadcn-ui/ui)
- NOT_FOUND: Brief not found (lobehub/lobehub)
- NOT_FOUND: Registry item "${itemName}" was not found. (shadcn-ui/ui)
- NOT_FOUND: Memory '{memory_id}' not found. (infiniflow/ragflow)
- NOT_FOUND: Topic ${topicId} not found (lobehub/lobehub)
- NOT_FOUND: Knowledge base not found (lobehub/lobehub)
- NOT_FOUND: Source document not found (lobehub/lobehub)
- NOT_FOUND: Skill not found (lobehub/lobehub)
- NOT_FOUND: Redis not found (Dokploy/dokploy)
- NOT_FOUND: Acceptance not found (lobehub/lobehub)
- NOT_FOUND: Agent group not found (lobehub/lobehub)
- NOT_FOUND: NOT_FOUND (thingsboard/thingsboard)
…and 77 more across the corpus — use search.
Honest provenance: generated on 2026-08-27 from AI-assisted analysis of the linked records. See how records are made.