JuliusBrussee/caveman · error

envelope: object kind is required

Error message

envelope: object kind is required

What it means

scopeAAD requires a non-empty Kind because the AAD binds the ciphertext to the type of object being encrypted, preventing ciphertext of one kind from being decrypted as another. The trimmed Kind was empty when SealForScope or OpenForScope was called. The library fails closed to preserve kind separation.

Solutions

  1. Set Scope.Kind to the object's type string (e.g. "document", "token", "webhook") at every seal/open call site
  2. Centralize scope construction in one helper that validates Kind before encryption
  3. Confirm the constant/variable holding Kind is actually initialized (check for shadowed or renamed identifiers)
  4. Ensure refactors didn't drop the Kind assignment in the struct literal

Example fix

// before
scope := envelope.Scope{OrganizationID: orgID, ProjectID: projID}
// after
scope := envelope.Scope{OrganizationID: orgID, ProjectID: projID, Kind: "document"}
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(kind) == "" {
    return errors.New("object kind required before envelope operations")
}

Type guard

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

Try / catch

_, _, err := /* SealForScope/OpenForScope */
if err != nil && strings.Contains(err.Error(), "object kind is required") {
    return ErrMissingObjectKind
}

Prevention

When it happens

Trigger: Calling SealForScope or OpenForScope with envelope.Scope{Kind: ""} or Kind containing only whitespace — e.g. the caller forgot to set Kind or passed a variable that was never initialized.

Common situations: A generic encrypt helper forwards a caller-supplied scope without validating Kind; refactors renamed the Kind constant but a call site still passes an empty string; test fixtures build 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/54d3c3df4dfb487d. Report an issue: GitHub.

Appendix: source

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

		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)