## TL;DR
GitLab masking only hides exact string matches; a value that gets base64-encoded, split across lines, or embedded in a larger string still prints. Treat any appearance as compromised: rotate the value, find how it leaked into the log, and move the secret to a file-type variable or external manager.

## Error
```text
gitlab ci: masked variable exposed in job log
```

## Steps
1. Rotate immediately: the value in the log is compromised, so generate a new secret at the provider and update the CI variable. Expected: the exposed value is dead.
2. Find the leak path: search the job log for the value's fragments and identify the command that printed it (often an echo of a derived value, a debug dump, or a tool's verbose output). Expected: one specific line or command identified.
3. Stop printing derived values: remove the echo, disable the verbose flag, or redact the output in the script. Expected: re-running the job no longer prints anything resembling the secret.
4. Switch to file-type variables for multiline or structured secrets so the value never passes through shell expansion. Expected: the secret reaches the job as a file, not an environment string.
5. For high-value secrets, move to an external secret manager fetched at runtime instead of a CI variable at all. Expected: the secret never lives in the CI variable store.

## When to use
- A masked variable (or its transformed version) appears in a job log
- Auditing CI logs for secret exposure
- Designing secret handling for a new GitLab pipeline

## When not to use
- Variable fails to save with a validation error (different problem)
- The variable is not injected at all (availability, not masking)

## Tool compatibility
- GitLab CI masked and file-type variables; external secret managers

## Variant phrasings
### gitlab masked variable visible after base64
Masking matches the raw value only; encoded or concatenated forms print freely. Rotate and stop encoding secrets into logs.

### secret leaked in gitlab job trace
Rotate first, then find the printing command; the trace is the evidence, not the fix.

## Why it happens
Masking is a log filter, not encryption: the runner replaces exact occurrences of the stored string at display time. Any transformation (encoding, splitting, embedding) defeats the filter, and the underlying value was already delivered to the job environment in full.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_ilCd6LyZLblnepR3Z6yXQw
