Tencent/WeKnora · warning

storage path workspace mismatch

Error message

storage path workspace mismatch

What it means

ValidateStoragePathTenant parsed a tenant ID from the storage path, but it does not match the authenticated caller's tenantID. This blocks cross-tenant reads/writes through presigned storage paths: the object belongs to a different workspace than the requester. It's an authorization failure, not a malformed path.

Source

Thrown at internal/utils/presign.go:121

}

// 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")
	}
	return nil
}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Verify the frontend isn't reusing stale/cached storage paths from a different tenant; fetch paths fresh from the API per tenant
  2. Ensure the caller passes the authenticated principal's tenantID (from the session/JWT), not a value taken from the request path or query string
  3. Use the KB-scoped flow (ValidateKBScopedStoragePath) if the resource is intentionally shared across tenants via a knowledge base
  4. If the object is truly misplaced, an admin should move it under the correct tenant prefix rather than relaxing the check

Example fix

// before: trusts client-supplied tenant
ten, _ := strconv.ParseUint(r.URL.Query().Get("tenant"), 10, 64)
err := ValidateStoragePathTenant(path, ten)
// after: uses authenticated principal
err := ValidateStoragePathTenant(path, session.TenantID)
Defensive patterns

Strategy: try-catch

Validate before calling

if tenantFromPath := utils.ParseTenantIDFromStoragePath(key); tenantFromPath != 0 && tenantFromPath != session.TenantID {
    return errors.New("requested object belongs to another workspace")
}

Type guard

func belongsToCallerTenant(key string, tenantID uint64) bool {
    return utils.ParseTenantIDFromStoragePath(key) == tenantID
}

Try / catch

err := utils.ValidateStoragePathTenant(key, session.TenantID)
if err != nil {
    if strings.Contains(err.Error(), "workspace mismatch") {
        log.Warn("cross-tenant storage access denied", "key", key, "caller_tenant", session.TenantID)
        http.Error(w, "not found", http.StatusNotFound) // don't leak existence
        return
    }
    http.Error(w, "bad request", http.StatusBadRequest)
}

Prevention

When it happens

Trigger: A user requesting a presigned URL for a file path whose embedded tenant segment belongs to another workspace — e.g. passed-in path /storage/42/files/a.pdf while the JWT's tenantID is 7, or a shared KB file accessed outside ValidateKBScopedStoragePath's allowed tenant set.

Common situations: Copy-pasted links between users of different workspaces; client code caching absolute storage URLs from another environment (staging vs prod with differing tenant IDs); bugs where the caller passes the resource owner's tenant instead of the authenticated principal's; IDOR probing attempts.

Related errors


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