jenkinsci/jenkins · error · IOException
Could not decode input
Error message
Could not decode input
What it means
IOException thrown by AnnotatedLargeText.createAnnotator when decoding the `X-ConsoleAnnotator` HTTP header (the browser→server progressive-console transport) raises a RuntimeException — bad base64, a cipher/GZIP failure, or a key mismatch on the PASSING_ANNOTATOR secret. The original exception is wrapped and propagated (it does not silently fall back to a fresh annotator).
Source
Thrown at core/src/main/java/hudson/console/AnnotatedLargeText.java:194
private void setContentTypeImpl(StaplerResponse2 rsp) {
rsp.setContentType(isHtml() ? "text/html;charset=UTF-8" : "text/plain;charset=UTF-8");
}
private ConsoleAnnotator<T> createAnnotator(StaplerRequest2 req) throws IOException {
try {
String base64 = req != null ? req.getHeader("X-ConsoleAnnotator") : null;
if (base64 != null) {
Cipher sym = PASSING_ANNOTATOR.decrypt();
try (ObjectInputStream ois = new ObjectInputStreamEx(new GZIPInputStream(
new CipherInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(base64.getBytes(StandardCharsets.UTF_8))), sym)),
Jenkins.get().pluginManager.uberClassLoader)) {
long timestamp = ois.readLong();
if (TimeUnit.HOURS.toMillis(1) > abs(System.currentTimeMillis() - timestamp))
// don't deserialize something too old to prevent a replay attack
return getConsoleAnnotator(ois);
} catch (RuntimeException ex) {
throw new IOException("Could not decode input", ex);
}
}
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
// start from scratch
return ConsoleAnnotator.initial(context);
}
@SuppressFBWarnings(value = "OBJECT_DESERIALIZATION", justification = "Deserialization is protected by logic.")
private ConsoleAnnotator getConsoleAnnotator(ObjectInputStream ois) throws IOException, ClassNotFoundException {
return (ConsoleAnnotator) ois.readObject();
}
@CheckReturnValue
@Override
public long writeLogTo(long start, Writer w) throws IOException {
if (isHtml())View on GitHub (pinned to 2e228ff40b)
Solutions
- Refresh the console page in the browser so a fresh annotator is issued.
- If proxied, ensure the reverse proxy forwards X-ConsoleAnnotator verbatim.
- For programmatic clients, omit the header (or send a freshly obtained one) rather than reusing an old value.
Defensive patterns
Strategy: try-catch
Try / catch
try {
annotator = createAnnotator(req);
} catch (IOException e) {
// header undecodable: start fresh instead of failing the page
annotator = ConsoleAnnotator.initial(context);
} Prevention
- Do not reuse stale X-ConsoleAnnotator header values across sessions/restarts.
- Ensure reverse proxies forward the header unchanged.
When it happens
Trigger: A console-output poll sends a malformed/stale X-ConsoleAnnotator header; or the Jenkins PASSING_ANNOTATOR secret changed (restart, resharded key, or restored backup) so the cipher stream cannot be decrypted.
Common situations: Browser tab left open across a Jenkins restart; a reverse proxy altering/stripping the header; replay/tampering; instance restored from backup with different secrets.
Related errors
- Refusing to deserialize unsigned note from an old log.
- MAC mismatch
- Zip ${zipFile.getPath()} contains illegal file name that bre
- invalid iconSize
- SHA-256 could not be instantiated, but is required to be imp
AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14).
Data as JSON: /api/errors/b8fe31ca5775b4a4.
Report an issue: GitHub.