Tencent/WeKnora · error

storage path has no tenant segment

Error message

storage path has no tenant segment

What it means

ValidateStoragePathTenant found no tenant segment in the storage path: ParseTenantIDFromStoragePath returned 0. The path must embed the owning tenant's ID (e.g. /storage/<tenantID>/...) so presigned-URL validation can confirm the caller's tenant matches. A path without this segment cannot be attributed to a tenant and is rejected defensively.

Source

Thrown at internal/utils/presign.go:118

	// Verify signature.
	expected := signPayload(key, filePath, tenantID, expires)
	return hmac.Equal([]byte(expected), []byte(sig))
}

// kbScopedExportsSegment is the only storage prefix served by the KB-scoped
// file proxy. Embedded wiki/chunk images land under exports/; raw knowledge
// uploads use {tenant}/{knowledgeID}/... and are served via
// /knowledge/{id}/download instead.
const kbScopedExportsSegment = "exports"

// ValidateStoragePathTenant ensures the tenant segment embedded in a provider://
// storage path matches the authenticated caller's tenant. Cross-tenant access
// for arbitrary tenant paths uses /api/v1/files/presigned with an HMAC bound to
// the resource owner; KB-scoped shared rendering uses ValidateKBScopedStoragePath.
func ValidateStoragePathTenant(filePath string, tenantID uint64) error {
	pathTenant := ParseTenantIDFromStoragePath(filePath)
	if pathTenant == 0 {
		return fmt.Errorf("storage path has no tenant segment")
	}
	if pathTenant != tenantID {
		return fmt.Errorf("storage path workspace mismatch")
	}
	return nil
}

// ValidateKBScopedStoragePath is used by GET /knowledge-bases/:id/files. It
// requires the path to belong to the KB owner tenant and to live under the
// exports/ namespace used for embedded images (SaveBytes / multimodal output).
// This prevents borrowers with shared-KB read access from using the proxy to
// fetch arbitrary owner-tenant objects such as raw knowledge uploads.
func ValidateKBScopedStoragePath(filePath string, tenantID uint64) error {
	if err := ValidateStoragePathTenant(filePath, tenantID); err != nil {
		return err
	}
	if !storagePathHasExportsScope(filePath, tenantID) {
		return fmt.Errorf("storage path is outside KB-scoped exports namespace")

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Re-upload or copy the object into the tenant-scoped layout /storage/<tenantID>/... using the normal upload API
  2. Fix the code/fixture constructing the path so it includes the tenant ID segment (use the shared path-builder helper)
  3. Check ParseTenantIDFromStoragePath's expected format and confirm your path matches (delimiters, numeric segment position)
  4. For legacy data, run a one-time migration rewriting object keys to include the tenant segment

Example fix

// before
key := fmt.Sprintf("uploads/%s", filename)
// after
key := fmt.Sprintf("storage/%d/uploads/%s", tenantID, filename)
Defensive patterns

Strategy: validation

Validate before calling

func pathHasTenantSegment(p string) bool {
    return utils.ParseTenantIDFromStoragePath(p) != 0
}
// guard before requesting presigned access
if !pathHasTenantSegment(storageKey) {
    return errors.New("storage key must be in /storage/<tenantID>/... layout")
}

Type guard

func isTenantScopedPath(p string) bool {
    parts := strings.SplitN(strings.TrimPrefix(p, "/"), "/", 3)
    if len(parts) < 2 || parts[0] != "storage" { return false }
    _, err := strconv.ParseUint(parts[1], 10, 64)
    return err == nil
}

Try / catch

err := utils.ValidateStoragePathTenant(key, session.TenantID)
if err != nil {
    if strings.Contains(err.Error(), "no tenant segment") {
        return fmt.Errorf("object %q is not tenant-scoped; re-upload or migrate it", key)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ValidateStoragePathTenant (directly or via ValidateKBScopedStoragePath) with a legacy path written before tenant-scoped layout was introduced, a malformed path, or a path built manually without the tenant component.

Common situations: Migrating pre-multitenancy objects stored under flat keys; objects uploaded by scripts bypassing the normal upload API; misconfigured storage base path stripping the tenant prefix; test fixtures using dummy paths like 'file.pdf'.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/705b9647d27cfa3c. Report an issue: GitHub.