NousResearch/hermes-agent · error · TraceRedactionError
Trace upload blocked: secret redaction failed, so the transc
Error message
Trace upload blocked: secret redaction failed, so the transcript may still contain credentials or other sensitive data. Fix the redactor or rerun with --no-redact only after manually reviewing the transcript.
What it means
Raised as TraceRedactionError from agent/trace_upload.py:72 when the shared redactor (agent.redact.redact_sensitive_text with force=True) throws while scrubbing trace text before upload. The upload is deliberately refused (fail-closed) because the transcript may still contain credentials. The underlying exception is logged with exc_info before the re-raise.
Source
Thrown at agent/trace_upload.py:72
def _now_iso() -> str:
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
def _redact(text: Any, enabled: bool) -> Any:
"""Redact secrets from a string body when redaction is enabled.
Non-strings pass through untouched. Uses Hermes' shared redactor with
``force=True`` so an upload always scrubs known secret shapes even if
the user disabled log redaction globally.
"""
if not enabled or not isinstance(text, str) or not text:
return text
try:
from agent.redact import redact_sensitive_text
return redact_sensitive_text(text, force=True)
except Exception as exc:
logger.warning("Trace upload redaction failed; refusing upload", exc_info=True)
raise TraceRedactionError(_REDACTION_BLOCKED_MESSAGE) from exc
def _content_to_blocks(content: Any, redact: bool) -> List[Dict[str, Any]]:
"""Normalize a message ``content`` field into Anthropic content blocks."""
if content is None:
return []
if isinstance(content, str):
return [{"type": "text", "text": _redact(content, redact)}]
if isinstance(content, list):
blocks: List[Dict[str, Any]] = []
for part in content:
if isinstance(part, dict):
ptype = part.get("type")
if ptype == "text":
blocks.append({"type": "text", "text": _redact(part.get("text", ""), redact)})
elif ptype in ("image_url", "image"):
# Keep a placeholder; the viewer renders text turns and we
# don't want to inline base64 blobs into a trace.View on GitHub (pinned to c896c09c42)
Solutions
- Read the WARNING log line 'Trace upload redaction failed; refusing upload' — the chained exception (exc_info=True) names the exact redactor failure; fix that in agent/redact.py.
- Reproduce standalone: from agent.redact import redact_sensitive_text; redact_sensitive_text(<failing text>, force=True) and fix whatever raises.
- Add a regression test for the failing input shape so the redactor stays exception-free.
- Only after manually reviewing the transcript for secrets, rerun with --no-redact as the message instructs — this is the documented escape hatch, not the default.
Example fix
// before (redactor crashes on some input -> upload refused)
return redact_sensitive_text(text, force=True)
// after (redactor itself must be fixed; keep fail-closed upload behavior)
// e.g. in agent/redact.py, guard the pattern application:
for pattern, repl in _SECRET_PATTERNS:
try:
text = pattern.sub(repl, text)
except re.error:
logger.exception("bad redaction pattern %s", pattern)
raise Defensive patterns
Strategy: try-catch
Validate before calling
from agent.redact import redact_sensitive_text
def redaction_is_healthy(sample: str) -> bool:
"""Run the redactor over representative text before starting a run."""
try:
redact_sensitive_text(sample, force=True)
return True
except Exception:
return False Try / catch
from agent.trace_upload import TraceRedactionError
try:
upload_trace(messages, redact=True)
except TraceRedactionError:
# upload was refused; never fall back to uploading unredacted text here.
log.error("trace upload skipped: redaction failed")
# surface to the user; only a human may decide on --no-redact after review Prevention
- Keep the redactor unit-tested against the secret shapes your traces actually contain.
- Never catch TraceRedactionError and retry with redact=False automatically — the block is a security control.
- Log the chained exception (exc_info) so redactor bugs are diagnosable from the warning line.
When it happens
Trigger: Calling the trace-upload path (build_trace_jsonl / upload helpers that call _redact(text, redact=True)) when redact_sensitive_text raises for any reason: a broken regex/pattern in agent/redact.py, an unexpected input type inside the redactor, or a partially broken install where agent.redact imports but fails at runtime.
Common situations: A change to the redaction patterns introduces an exception on certain payloads; a new secret shape (e.g. unusual token format) hits an untested branch; environment differences (locale, missing dependency used by the redactor) make the redactor crash only in CI or on another machine.
Related errors
- path could not be verified against the credential deny-list
- ${label} exited and ${dashboardIndexUrl(baseUrl)} is served
- Could not repair the desktop installation ID.
- Invalid external URL
- Invalid preview URL
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/e2897970ca8f5e67.
Report an issue: GitHub.