ipfs/kubo · warning

%s : %w

Error message

%s : %w

What it means

offlineErrWrap decorates errors from the gateway backend when the gateway runs in offline mode. If the underlying backend returns iface.ErrOffline (a required piece of content or IPNS record is not available locally and fetching is disabled), it rewrites the error as '<original message> : service unavailable' so the gateway can map it to an HTTP 503 for the client.

Source

Thrown at core/corehttp/gateway.go:206

	backend, err := gateway.NewBlocksBackend(bserv,
		gateway.WithValueStore(vsRouting),
		gateway.WithNameSystem(nsys),
		gateway.WithResolver(pathResolver),
	)
	if err != nil {
		return nil, err
	}
	return &offlineGatewayErrWrapper{gwimpl: backend}, nil
}

type offlineGatewayErrWrapper struct {
	gwimpl gateway.IPFSBackend
}

func offlineErrWrap(err error) error {
	if errors.Is(err, iface.ErrOffline) {
		return fmt.Errorf("%s : %w", err.Error(), gateway.ErrServiceUnavailable)
	}
	return err
}

func (o *offlineGatewayErrWrapper) Get(ctx context.Context, path path.ImmutablePath, ranges ...gateway.ByteRange) (gateway.ContentPathMetadata, *gateway.GetResponse, error) {
	md, n, err := o.gwimpl.Get(ctx, path, ranges...)
	err = offlineErrWrap(err)
	return md, n, err
}

func (o *offlineGatewayErrWrapper) GetAll(ctx context.Context, path path.ImmutablePath) (gateway.ContentPathMetadata, files.Node, error) {
	md, n, err := o.gwimpl.GetAll(ctx, path)
	err = offlineErrWrap(err)
	return md, n, err
}

func (o *offlineGatewayErrWrapper) GetBlock(ctx context.Context, path path.ImmutablePath) (gateway.ContentPathMetadata, files.File, error) {
	md, n, err := o.gwimpl.GetBlock(ctx, path)

View on GitHub (pinned to 329838acdf)

Solutions

  1. Preload the requested content locally: `ipfs get <cid>` or pin it (`ipfs pin add <cid>`) on the gateway node before serving.
  2. If the gateway should fetch on demand, disable offline mode: `ipfs config --json Gateway.NoFetch false` and restart the daemon.
  3. For IPNS names, publish/resolve while online once so the record is cached, or serve only DNSLink/record-backed names with records present.
  4. Treat the HTTP 503 on the client side as 'content not locally available' and retry after the gateway operator preloads it.

Example fix

// before: offline gateway, content missing -> 503
// Gateway.NoFetch = true; request /ipfs/<unpinned-cid>

// after: preload then serve
ipfs config --json Gateway.NoFetch false  # or:
ipfs pin add <cid>                        # ensure local availability
Defensive patterns

Strategy: fallback

Validate before calling

// client-side preflight for an offline gateway
hasLocal, err := hasLocally(api, cid) // e.g. `ipfs dag stat --local` or blockstore check
if err != nil { return err }
if !hasLocal {
    return fmt.Errorf("gateway is offline (NoFetch=true); content %s must be pinned locally first", cid)
}

Type guard

func isOfflineUnavailable(err error) bool {
    // surfaced as HTTP 503 with the original message + 'service unavailable'
    return errors.Is(err, gateway.ErrServiceUnavailable) ||
        errors.Is(err, iface.ErrOffline)
}

Try / catch

md, resp, err := backend.Get(ctx, p)
if err != nil {
    if errors.Is(err, gateway.ErrServiceUnavailable) || errors.Is(err, iface.ErrOffline) {
        return nil, fmt.Errorf("content unavailable offline: %w", err) // map to 503 / trigger pinning
    }
    return err
}

Prevention

When it happens

Trigger: Requesting a path through an HTTP gateway (Get, Head, GetCAR, ResolvePath, GetAll, GetBlock) when `Gateway.NoFetch` is true (offline gateway) and the blocks or IPNS record needed to serve the path are not present in the local datastore.

Common situations: Operating a public gateway with NoFetch=true and clients requesting content that was never added or pinned locally; IPNS name resolution on an offline gateway with no cached record; trustless/CAR requests for blocks absent from the store; misconfigured gateways where operators expect fetch-on-demand but NoFetch is enabled.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/898f18f1b396b466. Report an issue: GitHub.