{"record":{"id":"1640d311187c05d9","repo":"caddyserver/caddy","slug":"syntax-error-unexpected-token-s-expecting-s","errorCode":null,"errorMessage":"syntax error: unexpected token '%s', expecting '%s', at %s:%d import chain: ['%s']","messagePattern":"syntax error: unexpected token '(.+?)', expecting '(.+?)', at (.+?):(.+?) import chain: \\['(.+?)'\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"caddyconfig/caddyfile/dispenser.go","lineNumber":423,"sourceCode":"\td.nesting = 0\n}\n\n// ArgErr returns an argument error, meaning that another\n// argument was expected but not found. In other words,\n// a line break or open curly brace was encountered instead of\n// an argument.\nfunc (d *Dispenser) ArgErr() error {\n\tif isOpenCurlyBrace(d.Token()) {\n\t\treturn d.Err(\"unexpected token '{', expecting argument\")\n\t}\n\treturn d.Errf(\"wrong argument count or unexpected line ending after '%s'\", d.Val())\n}\n\n// SyntaxErr creates a generic syntax error which explains what was\n// found and what was expected.\nfunc (d *Dispenser) SyntaxErr(expected string) error {\n\tmsg := fmt.Sprintf(\"syntax error: unexpected token '%s', expecting '%s', at %s:%d import chain: ['%s']\", d.Val(), expected, d.File(), d.Line(), strings.Join(d.Token().imports, \"','\"))\n\treturn errors.New(msg)\n}\n\n// EOFErr returns an error indicating that the dispenser reached\n// the end of the input when searching for the next token.\nfunc (d *Dispenser) EOFErr() error {\n\treturn d.Errf(\"unexpected EOF\")\n}\n\n// Err generates a custom parse-time error with a message of msg.\nfunc (d *Dispenser) Err(msg string) error {\n\treturn d.WrapErr(errors.New(msg))\n}\n\n// Errf is like Err, but for formatted error messages\nfunc (d *Dispenser) Errf(format string, args ...any) error {\n\treturn d.WrapErr(fmt.Errorf(format, args...))\n}\n","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/caddyfile/dispenser.go#L405-L441","documentation":"The current STEK in storage passed its NextRotation deadline, so the provider called rotateKeys to generate and persist new keys, and rotation failed. This happens on the background rotation timer, meaning existing keys keep working but no new resumption keys are issued until rotation succeeds.","triggerScenarios":"Timer fires, getSTEK runs, time.Now().After(dstek.NextRotation) is true, and rotateKeys fails at either key generation or storage.Store of the new STEK gob.","commonSituations":"Storage became read-only or unreachable after initial provisioning; disk full on the data volume; storage credentials expired between rotations; a schema change in distributedSTEK gob encoding after a mixed-version cluster upgrade.","solutions":["Read the wrapped error to distinguish generation vs 'storing STEK gob' storage failures.","Free disk space / restore write access / refresh storage credentials on the node.","In mixed-version clusters, upgrade all instances together so the persisted gob schema matches.","Restart Caddy after fixing storage; the next rotation attempt will succeed and resume normal rotation scheduling."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"Background rotation failure: existing keys remain valid, so log and alert rather than crash; rotation retries on subsequent ticks once storage recovers.","preventionTips":["Monitor disk space and storage write health on nodes using distributed STEKs.","Alert on repeated rotation failures so resumption-key rotation does not silently stall.","Keep storage credentials valid across long uptimes (rotation errors can appear 7+ days after a credential lapse)."],"tags":["caddy","caddytls","stek","rotation","storage"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}