JuliusBrussee/caveman · error

envelope: organization scope is required

Error message

envelope: organization scope is required

What it means

scopeAAD refuses to build the additional authenticated data because the Scope has an empty OrganizationID after trimming whitespace. Envelope encryption binds ciphertext to a tenant scope, so sealing or opening without an organization would produce or accept data with no tenant binding. The library fails closed instead.

Solutions

  1. Populate Scope.OrganizationID with the authenticated tenant's ID before calling SealForScope/OpenForScope
  2. Trim/validate org context upstream (middleware) and reject requests lacking it before reaching the envelope layer
  3. Check struct-literal field ordering — Go unkeyed literals bind positionally; use keyed fields
  4. Fix test fixtures to use a real, non-empty organization ID

Example fix

// before
scope := envelope.Scope{"", projectID, "document"}
// after
if strings.TrimSpace(orgID) == "" { return errors.New("org required") }
scope := envelope.Scope{OrganizationID: orgID, ProjectID: projectID, Kind: "document"}
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(orgID) == "" {
    return errors.New("organization id required before envelope operations")
}

Type guard

func scopeHasOrg(s envelope.Scope) bool { return strings.TrimSpace(s.OrganizationID) != "" }

Try / catch

aad, id, err := /* via SealForScope/OpenForScope */
if err != nil && strings.Contains(err.Error(), "organization scope is required") {
    return ErrMissingTenantContext
}

Prevention

When it happens

Trigger: Calling SealForScope or OpenForScope with envelope.Scope{OrganizationID: ""} or one containing only whitespace (e.g. from an unset request header or empty tenant context).

Common situations: A request missing tenant context reaches the crypto layer; auth middleware did not populate the org ID; a struct literal was built with fields in the wrong order; tests pass a zero-value Scope.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/b8d28f06957c7cf3. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/envelope/envelope.go:173

	}
	ns := gcm.NonceSize()
	if len(ciphertext) < ns {
		return nil, fmt.Errorf("envelope: ciphertext too short")
	}
	nonce, ct := ciphertext[:ns], ciphertext[ns:]
	plaintext, err := gcm.Open(nil, nonce, ct, aad)
	if err != nil {
		return nil, fmt.Errorf("envelope: open: %w", err)
	}
	return plaintext, nil
}

func scopeAAD(scope Scope) ([]byte, string, error) {
	scope.OrganizationID = strings.TrimSpace(scope.OrganizationID)
	scope.ProjectID = strings.TrimSpace(scope.ProjectID)
	scope.Kind = strings.TrimSpace(scope.Kind)
	if scope.OrganizationID == "" {
		return nil, "", fmt.Errorf("envelope: organization scope is required")
	}
	if scope.Kind == "" {
		return nil, "", fmt.Errorf("envelope: object kind is required")
	}
	aad, err := json.Marshal(struct {
		Version        int    `json:"version"`
		OrganizationID string `json:"organization_id"`
		ProjectID      string `json:"project_id"`
		Kind           string `json:"kind"`
	}{2, scope.OrganizationID, scope.ProjectID, scope.Kind})
	if err != nil {
		return nil, "", fmt.Errorf("envelope: encode scope: %w", err)
	}
	sum := sha256.Sum256(aad)
	return aad, hex.EncodeToString(sum[:]), nil
}

View on GitHub (pinned to 3ee70a1026)