goharbor/harbor · error
the system is in read only mode, cancel the request
Error message
the system is in read only mode, cancel the request
What it means
Sentinel error returned by the read-only interceptor when Harbor is configured in read-only mode. NewInterceptor wraps requests with a short-TTL (5s) in-memory cache of the read-only config flag; when the flag is set, mutating requests are intercepted and cancelled with this error. It is a deliberate system-state rejection, not a bug.
Source
Thrown at src/pkg/registry/interceptor/readonly/interceptor.go:31
// limitations under the License.
package readonly
import (
"context"
"errors"
"net/http"
"time"
"github.com/goharbor/harbor/src/lib/cache"
"github.com/goharbor/harbor/src/lib/cache/memory"
"github.com/goharbor/harbor/src/lib/config"
itcp "github.com/goharbor/harbor/src/pkg/registry/interceptor"
)
// Err indicates the system is in read only mode
var (
Err = errors.New("the system is in read only mode, cancel the request")
key = "read-only"
)
// NewInterceptor creates an interceptor that intercepts any requests if the system is set to read-only
func NewInterceptor() itcp.Interceptor {
// ignore the error as the New return nil error
cache, _ := memory.New(cache.Options{
Expiration: 5 * time.Second,
Codec: cache.DefaultCodec(),
})
return &interceptor{cache: cache}
}
type interceptor struct {
cache cache.Cache
}
func (i *interceptor) Intercept(req *http.Request) error {View on GitHub (pinned to 7b2fd08cc5)
Solutions
- Wait for the maintenance window to end, then retry the operation; the interceptor cache expires in 5 seconds so the system recovers as soon as the flag is cleared
- Check and clear the read-only setting via Harbor configuration (project config / system settings) if it was left on accidentally
- If storage-full caused it, free registry storage or raise the threshold before clearing the flag
- Ensure background jobs (replication, scan) are paused or tolerate deferral while read-only is on
Example fix
// before
if err := deleteTag(tag); err != nil {
return err // surfaces during read-only window
}
// after
maxAttempts := 10
for i := 0; i < maxAttempts; i++ {
err := deleteTag(tag)
if err == nil || !errors.Is(err, readonly.Err) {
break
}
time.Sleep(30 * time.Second) // retry after read-only is lifted
} Defensive patterns
Strategy: retry
Validate before calling
// Check read-only state before issuing writes (mirror of interceptor cache)
if config.ReadOnly() {
return readonly.Err
} Try / catch
if err := op(); err != nil && errors.Is(err, readonly.Err) {
// schedule retry with backoff; system is read-only for a bounded window
time.AfterFunc(time.Minute, func() { _ = op() })
} Prevention
- Surface a user-facing banner when the system is read-only so clients stop writing
- Pause replication/scan jobs during maintenance windows
- Monitor registry storage free space to avoid automatic read-only activation
When it happens
Trigger: Any write-style API call (push, tag deletion, config change, project create) routed through the registry proxy while the read-only setting is enabled in Harbor's configuration; also triggered automatically when Harbor switches itself to read-only, e.g. during failed free-space checks or database maintenance windows.
Common situations: Harbor entered read-only mode because the registry storage filled up past the threshold; an admin enabled read-only for maintenance/upgrade; jobs (GC, replication, scanning) retrying writes during the window; users pushing images during migration.
AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16).
Data as JSON: /api/errors/d0f0532fffc4ecd8.
Report an issue: GitHub.